Java ReentrantLock 源码阅读笔记(下)
在前一篇文章中我们分析了 ReentrantLock 的 lock 和 unlock 的实现,没有看过的同学可以点击这里.
今天我们继续来分析 ReentrantLock 的 Condition 的实现。
Condition 实例创建
Condition 是一个接口:
public interface Condition {
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}
我们创建 Condition 实例是通过 ReentrantLock#newCondition() 方法实现的:
public Condition newCondition() {
return sync.newCondition();
}
// ...
final ConditionObject newCondition() {
return new ConditionObject();
}
// ...
Condition 的实现类是 ConditionObject,它是 AbstractQueuedSynchronizer 中的一个内部类,后面我们的代码分析也是基于 ConditionObject#await() 方法和 ConditionObject#notify() 方法的实现,当读懂了这两个方法后,其他的方法也都好理解了。
在开始关键的代码阅读之前我先大体的讲一下 CondtionObject 的工作原理,在前面的文章中我已经介绍过了,获取锁失败的线程会被添加到 AbstractQueuedSynchronizer 的等待队列中,等待获取锁的线程释放锁后,再依次把锁交给等待队列中的线程,直到等待队列中的线程都完成任务。
当获取到锁的线程可以通过 ConditionObject#await() 方法去释放锁(没有获取到锁调用会抛出异常),并把当前线程添加到 CondtionObject 的等待队列中,当其他获取到锁的线程调用 ConditionObject#notify() 后(同样没有获取到锁就调用会抛出异常)会将其中一个 CondtionObject 等待队列中的线程移动到 AbstractQueuedSynchronizer 的等待队列中去,等待别的线程都释放了锁后,就会继续恢复 ConditionObject#await() 方法的执行。
await() 方法实现
我们直接看 ConditionObject#await() 方法:
public final void await() throws InterruptedException {
// 检查线程是否中断,如果中断直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 在 Condtion 中添加一个等待的 node
Node node = addConditionWaiter();
// 释放当前线程持有的锁,如果没有持有锁会抛出异常
int savedState = fullyRelease(node);
int interruptMode = 0;
// 死循环检查是否从 Condition 的等待队列中移动到 AQS 队列中,如果已经移动跳出循环
while (!isOnSyncQueue(node)) {
// 暂停当前线程
LockSupport.park(this);
// 检查线程是否中断,如果中断直接退出
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 这里表示已经被移动到 AQS 队列中了,通过 acquireQueued() 方法来等待其他的线程释放锁,这方法在上一篇文章中已经分析过了
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 移除等待过程中被删除的其他节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
// 检查线程中断状态
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
在理一下上面代码的逻辑:
-
检查当前线程是否中断,如果中断直接抛出异常(这里再多插一嘴,在
Java中线程在运行中是无法被直接打断的,当需要中断线程的时候会给线程一个interrupted标识,然后在别的操作中就会检查这个标识,如果已经标识为中断,就会直接抛出InterruptedException异常去中断线程,Java代码中有很多地方都会检查这个标识)。 -
通过
addConditionWaiter()方法向Condition队列中添加一个等待节点。 -
通过
fullyRelease()方法释放当前线程持有的锁,如果当前线程没有持有锁,会抛出异常。 -
通过
isOnSyncQueue()方法死循环检查是否从Condition等待队列中已经移动到AbstractQueuedSynchronizer的等待队列中(signal()方法会做这个操作,后面我们会看到),如果还在Condition的等待队列中就会调用LockSupport.park()方法暂停当前线程。 -
然后通过
acquireQueued()方法等待AbstractQueuedSynchronizer()将锁给到当前线程,上一篇文章中已经分析过这个方法,本篇文章不再分析。
我们来看看 addConditionWaiter() 方法是如何向 Condition 队列中添加一个等待节点:
private Node addConditionWaiter() {
// 如果当前线程没有获取到锁,抛出异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
// 移除已经被删除的节点
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
// 注意初始化的 Node 的 waitStatus 是 CONDITION,lock 中默认是 0。
Node node = new Node(Node.CONDITION);
// 将节点添加到 Condition 队列的尾部
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
这里要注意和 lock() 的队列做一个区分,lock() 方法中的 Node 的 waitStatus 是 0,而这里是 Node#CONDITION;lock() 方法中链表是双向链表用的变量是 Node#prev 和 Node#next,而这里是单向链表用用的变量是 Node#nextWaiter。
继续看看释放锁的方法 fullRelease():
final int fullyRelease(Node node) {
try {
int savedState = getState();
if (release(savedState))
return savedState;
throw new IllegalMonitorStateException();
} catch (Throwable t) {
node.waitStatus = Node.CANCELLED;
throw t;
}
}
朴实无华的代码,就不多聊了,release() 方法在上一篇文章中已经分析过了。
再来看看 isOnSyncQueue() 方法是如何判断当前线程的节点已经被移动到了 AbstractQueuedSynchronizer 队列中:
final boolean isOnSyncQueue(Node node) {
// 如果 waitStatus 是 CONDITION 或者他的前一个节点是空就表示在 Condition 的队列中。
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// 如果 next 不为空表示已经在 AQS 的队列中
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
// 最后的判断方法是去 AQS 队列中一个一个找,是否有这个节点。
return findNodeFromTail(node);
}
signal() 方法实现
OK,我们趁热打铁,继续看看 signal() 方法是如何将 Condition 队列中的节点移动到 AbstractQueuedSynchronizer 中去的:
public final void signal() {
// 同样先判断当前线程是否持有锁。
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 拿到 Condition 队列中的第一个节点,进入 doSignal() 方法.
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
继续跟踪 doSignal() 方法:
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
doSignal() 方法会通过 transferForSignal() 移动一个节点到 AbstractQueuedSynchronizer 队列中去,如果移动失败,就再移动下一个。
继续看 transferForSignal() 方法的实现:
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
// 将 waitStatus 通过 CAS 的方式从 CONDITION 修改成 0,修改失败直接返回.
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
// 将节点添加到 AQS 中,返回的 p 是 node 的前一个节点.
Node p = enq(node);
int ws = p.waitStatus;
// 将前一个节点的 waitStatus 修改成 SIGNAL。
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
// 恢复 await 方法对应的线程
LockSupport.unpark(node.thread);
return true;
}
继续看看 enq() 方法移动到 AbstractQueuedSynchronizer 队列的具体实现:
private Node enq(Node node) {
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return oldTail;
}
} else {
initializeSyncQueue();
}
}
}
这段代码和 lock() 方法中的入队列的方式一样,朴实无华。
最后
其实 ReentrantLock 的 Condition 中的 await() / signal() 和 synchronized 中 Object 中的 wait() / notify() 逻辑几乎一样,只是实现的代码不一样而已,一个是在标准库中用 Java 的实现,一个是虚拟机中用 C/C++ 实现。希望通过这两篇文章能够让你能够更加深入理解 ReentrantLock,不再是人云亦云。