类图
ReentrantLock相关类图:
AbstractOwnableSynchronizer类保持和获取独占线程。
AbstractQueuedSynchronizer,继承自AbstractOwnableSynchronizer,简称AQS,基于FIFO(First Input First Output)队列的实现。以虚拟队列的方式管理线程的锁获取与锁释放,以及各种情况下的线程中断。提供了默认的同步实现,但是获取锁和释放锁的实现定义为抽象方法,由子类实现。目的是使开发人员可以自由定义获取锁以及释放锁的方式。
Sync是ReentrantLock的内部抽象类,继承自AbstractQueuedSynchronizer,实现了简单的获取锁和释放锁。NonfairSync和FairSync分别表示“非公平锁”和“公平锁”,都继承于Sync,并且都是ReentrantLock的内部类。
ReentrantLock实现了Lock接口的lock-unlock方法,根据fair参数决定使用NonfairSync还是FairSync。
AQS
ReentrantLock实现的前提就是AbstractQueuedSynchronizer,简称AQS,是java.util.concurrent的核心,CountDownLatch、FutureTask、Semaphore、ReentrantLock等都有一个内部类是这个抽象类的子类。
Node
Node是AQS的内部类,是对每一个访问同步代码的线程的封装。不仅包括了需要同步的线程,而且也包含了每个线程的状态,比如等待解除阻塞,等待条件唤醒,已经被取消等等。同时Node还关联了前驱和后继,即prev和next。
多个Node连接起来成为了虚拟队列(因为不存在真正的队列容器将每个元素装起来所以说是虚拟的,我把它称为release队列,意思是等待释放),其实就是一个有头有尾的双向链表结构:
state
是AQS的一个成员变量,用来记录锁的持有情况:
没有线程持有锁的时候,state为0。
当某个线程获取锁时,state的值增加,具体增加多少开发人员可自定义,默认为1,表示该锁正在被一个线程占有。
当某个已经占用锁的线程再次获取到锁时,state再增长,此为重入锁。
当占有锁的线程释放锁时,state也要减去当初占有时传入的值,默认为1。
多个线程竞争锁的时候,state必须通过CAS进行设置,这样才能保证锁只能有一个线程持有。
Sync
NonfairSync和FairSync分别表示“非公平锁”和“公平锁”,都继承于Sync,并且都是ReentrantLock的内部类。
我们来看NonfairSync获取锁的代码:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
还有FairSync获取锁的代码:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
其实差别就在于NonfairSync获取锁时比FairSync中少了一个判断!hasQueuedPredecessors()
,hasQueuedPredecessors()中判断了是否存在等待队列,导致公平锁和非公平锁的差异如下:
公平锁:公平锁讲究先来先到,线程在获取锁时,如果这个锁的等待队列中已经有线程在等待,那么当前线程就会进入等待队列中。
非公平锁:不管是否有等待队列,如果可以获取锁,则立刻占有锁对象。
lock()与unlock()
lock()
流程图(图中的Node0和Node1在源代码中不存在,是为了方便说明清楚才添加的别称):
unlock()
流程图:
参考:
1.ReentrantLock实现原理深入探究
2.ReentrantLock的lock-unlock流程详解
3.轻松学习java可重入锁(ReentrantLock)的实现原理
4.ReentrantLock解析
5.深度解析Java8 – AbstractQueuedSynchronizer的实现分析(上)
6.ReentrantLock(重入锁)以及公平性