前言
在详解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
方法,它虽然是一个抽象类,但是里面并没有抽象方法。