在AbstractQueuedSynchronizer中,有两个FIFO队列,一个是同步队列,用来排队申请同步状态,还有一个是条件等待队列,当调用了await()系列的方法后,就会在等待队列尾部插入一个节点,通知唤醒的时候会把这个节点从等待队列转移到同步队列。
本文主要描述条件等待队列以及等待、通知机制的实现,关于同步队列的相关操作和实现分析,可以在这篇博客了解。
ReentrantLock可以实现条件等待,我们可以先调用newCondition()方法生成一个条件对象ConditionObject,然后调用ConditionObject的await()方法即可实现条件等待。
一路跟踪源码到AbstractQueuedSynchronizer内部类ConditionObject的await()方法。
/**
* Implements interruptible condition wait.
*
* - If current thread is interrupted, throw InterruptedException.
*
- Save lock state returned by {@link #getState}.
*
- Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
*
- Block until signalled or interrupted.
*
- Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
*
- If interrupted while blocked in step 4, throw InterruptedException.
*
*/
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//在等待队列的尾部添加一个节点
Node node = addConditionWaiter();
//释放同步队列中当前节点持有的同步状态,在这里会将同步队列中对应的节点移除
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
//如果不在同步队列里,阻塞当前线程,等待唤醒
LockSupport.park(this);
//线程被唤醒,是否把这个节点添加到同步队列中,检查当前线程的interrupt中断标记
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//在同步队列中进行自旋等待同步状态
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//清除无效的等待节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//处理中断标记
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
首先进行的是addConditionWaiter()方法。
/**
* Adds a new waiter to wait queue.
* @return its new wait node
*/
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
这个方法中,先取到最后一个等待节点t,如果t不为null或者t的状态不为CONDITION,触发一次“删除取消等待的节点”操作,也就是unlinkCancelledWaiters()方法,只要节点的状态不为CONDITION,该节点就可以被认为取消等待了(等待结束)。删除结束,就新创建一个CONDITION状态的节点加入到队列末尾。注意条件等待队列和同步队列中的不同,条件等待队列中的节点只知道它的下一个节点,并不知道它的上一个节点。
到这里addConditionWaiter()方法结束,此时已经增加了一个节点到条件等待队列,并且将新增加的节点返回。再回到await()方法,接下来需要同步队列中当前持有同步状态的节点(头部节点,对应了当前线程)进行资源的释放,也就是fullyRelease()方法,进入fullyRelease(),其实就是调用了release()方法,并返回了释放前的加锁的次数。release()方法解析参见这篇博客。
再返回到await()方法,接下来是一个while循环,判断这个节点是否在同步队列上,如果不在,就进入循环体,通过LockSupport.park()阻塞线程,当线程被唤醒,执行while中的if判断。
/**
* Checks for interrupt, returning THROW_IE if interrupted
* before signalled, REINTERRUPT if after signalled, or
* 0 if not interrupted.
*/
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
/**
* Transfers node, if necessary, to sync queue after a cancelled wait.
* Returns true if thread was cancelled before being signalled.
*
* @param node the node
* @return true if cancelled before the node was signalled
*/
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
如果当前线程被中断,则调用transferAfterCancelledWait决定外层await()方法是应该抛出InterruptedException异常还是重新调用中断方法。transferAfterCancelledWait()方法中,如果CAS设置成功,说明中断之前没有调用signal()方法(因为signal方法会对节点状态进行CAS设置,从CONDITION到0),将当前节点添加到同步队列;否则,循环检查当前节点是否在同步队列中,如果不在,就把CPU让出来,直到被放到同步队列上时,中止循环,返回false。
然后回到await()方法,调用acquireQueued(),acquireQueued()中进行自旋等待同步资源(这篇博客),让被唤醒的节点进行排队,并处理中断标记,如果有下一个条件等待者,这里会触发一次“取消等待节点清理”操作。再最后根据中断标记决定是抛出中断异常,还是仅仅调用中断方法。
接着在来看signal()方法,signal()方法唤醒等待的线程后,while中LockSupport.park(this);之后的代码才可以执行。
/**
* Removes and transfers nodes until hit non-cancelled one or
* null. Split out from signal in part to encourage compilers
* to inline the case of no waiters.
* @param first (non-null) the first node on condition queue
*/
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
/**
* Removes and transfers all nodes.
* @param first (non-null) the first node on condition queue
*/
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
从signal()方法跟踪到doSignal()方法,这里主要进行的操作是,将等待队列中的第一个等待者移动到同步队列,当成一个全新的尾节点来进行处理,具体流程也是增加到尾节点、然后进行自旋获取(这篇博客),并且这里将firstWaiter指向原本firstWaiter的下一个等待者,使原本的firstWaiter成为了一个垃圾对象(可被回收)。
signalAll方法也很好理解,直接将所有的等待者唤醒。