您的位置:首页 > 其它

详解JUC之锁——Lock与AQS(02)

2017-05-11 17:07 267 查看

前言

详解JUC之锁——概述(01)中我对JUC中的锁进行了概述,下面我就介绍一下它们的根基
Lock
接口和
AQS


Lock

看名字就知道
Lock
接口就是JUC中锁的顶级接口,支持语义不同的锁规则,比如说公平锁和非公平锁,独占锁(也可以叫互斥锁)和共享锁等。

它最主要的两个方法就是
lock()
unlock()
,一看就知道是获取锁和释放锁。

还有一个比较有趣的方法是
boolean tryLock(long time, TimeUnit unit)
方法,这个方法没有
lock()
方法那么死心眼,它在尝试获取锁,获取到了就返回
true
,一段时间获取不到就算了,返回一个
false


当然了,还有
Condition newCondition()
方法,返回与之关联的
Condition
对象

AbstractQueuedSynchronizer

AbstractQueuedSynchronizer
就是大名鼎鼎的AQS类,怎么说呢,这个类就是JUC锁的根基,是JUC中不同锁的共同抽象类,锁的许多公共方法都是在这个类中实现的,我给你看看它类继承结构你就知道了



有没有发现,无论是带有
Lock
后缀的类如
ReentrantLock
还是不带
Lock
后缀的类如
Semaphore
它们里面的内部类
Sync
都继承了
AQS
。其实JUC中的各种锁只是一个表面装饰,它们里面真正实现功能的还是
Sync


我再来给你看一个比根基更根基的东西,那就是
AQS
类的一个成员变量
state


/**
* The synchronization state.
*/
private volatile int state;


看注释说这个就是同步状态,可能到现在你还没有概念。那我问你,锁最主要的操作是什么?不就是获取锁释放锁嘛!!上面我讲了JUC中不同锁的很多公共方法是在
AQS
类实现的,其中就有获取锁和释放锁的方法,而我可以大声地告诉你,JUC中所有锁的获取锁和释放锁操作都是围绕着这个同步状态
state
进行加减操作。每次一个线程获取到锁即对应着这个
state
加1,释放锁就对应着这个
state
减1(
Semaphore
就特殊点,它能加n和减n),
state
为0的时候代表锁空闲。

公平锁和非公平锁

从上面我给出的
AQS
的继承结构图你会发现,很多锁的名叫
Sync
的内部类继承了
AQS
类,而这个
Sync
又分别有
FairSync
NonfairSync
类继承它,看名字就知道它们分别是公平锁和非公平锁了。

那公不公平的准则又是什么呢?

说到这个得先介绍一下CLH队列,CLH队列是AQS中“等待锁”的线程队列,比如说独占锁被一个线程占用着,其它线程就必须等待咯,这些线程就是在CLH队列等待的。这个队列的是用链表实现的,你可以看到
AQS
类有个内部类
Node
,这个就是链表的节点。CLH是通过自旋+CAS保证节点插入和移除的原子性(这个我在介绍原子类的时候说过类似的),就是说往里面插入或移除一个节点的时候,在并发条件下不会有问题。

至于CLH是什么意思,在
Node
的注释上有
"CLH" (Craig, Landin, and Hagersten) lock queue
,目测是三个人的名字缩写,应该是造出它的人吧。

那现在就可以回答公平的准则了。所谓公平,就是大家排队,是按照CLH队列先来先得的规则,即使锁没被任何线程持有,只要一个线程不是处于队头,它也会乖乖地等,公平地获取锁;非公平,那就是插队咯,当线程要获取锁时,它会无视CLH等待队列而直接获取锁,如果锁没有被任何线程持有,那不管它在CLH的那个角落,它都直接获取锁,没什么道德可言。

AbstractQueuedLongSynchronizer和AbstractOwnableSynchronizer

至于
AbstractQueuedLongSynchronizer
类跟
AbstractQueuedSynchronizer
类差不多,只不过它里面的
state
long
类型的,而
AbstractQueuedSynchronizer
的是
int
类型的,而且它们都继承了
AbstractOwnableSynchronizer
类——一个记录当前持有独占锁的线程的抽象类,这个类很简单,维护一个
Thread
类型变量,这个应该就是记录当前持有独占锁的线程,然后提供它的
getter
setter
方法,它虽然是一个抽象类,但是里面并没有抽象方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息