await()
在java多线程中,有多种方式等待、唤醒,有wait()和notify以及await()和signal(),wait()和notify()是在Object中使用的,通常用于synchronized,而await()和signal()通常在lock()中使用。线程在获得锁的前提下,调用await()会使线程释放锁并加入到Condition等待队列阻塞,直到接收到signal()命令,重新加入到同步队列。除了使用方法上的不同,两种还有特性上的不同,在等待队列中的线程可以响应中断,而wait()不支持;可以创建多个condition等待队列,而wait()不支持。
下面我们就来分析一下await()和signal()的源码
/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter(); //加入condition等待队列
int savedState = fullyRelease(node); //释放节点上的锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) { //不在同步队列上就一直循环
LockSupport.park(this); //挂起当前线程
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) //检查在condition等待队列期间有无被中断
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //释放同步队列的节点
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters(); //清除condition队列上waitStatus不为CONDITION的节点
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
从中可以看出,await()方法就是将节点放入condition队列中,然后释放该线程上的锁,然后这段程序会一直循环判断该节点是否在同步队列中,如果不在同步队列中,就会park挂起该线程并检查该线程是否中断,并进入下一次循环,如果在同步队列中(表明可能调用了signal()通知了线程),会调用acquireQueued()方法使同步队列中的节点获得锁。简单的用一句话概括就是,使用await()会让节点释放锁并加入condition等待队列,直到其他线程调用signal()或者达到定时的时间,加入同步队列获得锁。
接下来看看addConditionWaiter()方法,这个方法就是将节点加入condition等待队列中。
/**
* 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) { //如果lastWaiter的ws不为condition,就清除condition队列中不为condition的节点
unlinkCancelledWaiters();
t = lastWaiter; //重新赋值一个ws为condition的节点为lastWaiter
}
Node node = new Node(Thread.currentThread(), Node.CONDITION); //将该节点waitStatus设为CONDITION并加入等待队列
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
然后,调用fullyRelease()释放线程上的锁,需要注意的是,如果该线程没有获得锁,会抛出IllegalMonitorStateException,如果释放锁失败了,会将节点的waitStatus设为CANCELLED
/**
* Invokes release with current state value; returns saved state.
* Cancels node and throws exception on failure.
* @param node the condition node for this wait
* @return previous sync state
*/
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
接下来看看判断节点是否在同步队列中的方法,isOnSyncQueue()
// Internal support methods for Conditions
/**
* Returns true if a node, always one that was initially placed on
* a condition queue, is now waiting to reacquire on sync queue.
* @param node the node
* @return true if is reacquiring
*/
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
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.
*/
return findNodeFromTail(node);
}
在这个方法中,我们可以看到如果节点的next()不为空就认为该节点在同步对列中,但节点的prev不为空,就不能作为节点在同步队列的依据。方法中的注释中说到,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.大概意思就是cas放置该节点有可能会失败,所以必须的遍历队列来确定节点在队列中。明显可知findNodeFromTail()就是从后往前遍历队列,确定节点是否在队列中。
如果节点不在同步队列中,会调用LockSupport.park(this) 挂起当前线程,并且调用checkInterruptWhileWaiting()来查看在等待队列期间是否被中断,如果被中断则退出循环
/**
* 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)) { //此时其他线程还没有调用signal()
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)) //其他线程调用了signal(),现在需要等待这个线程尾插进同步队列成功
Thread.yield();
return false;
}
从中我们可以看出,当中断线程时,此时其他线程还没有调用signal(),将线程加入同步队列并interruptMode会被赋值为THROW_IE,其他线程调用了signal(),等待线程尾插同步队列成功并线程interruptMode会被赋值为REINTERRUPT。
在await()方法的最后,如果condition等待队列中还有其他节点,会调用unlinkCanceledWaiters()方法清除等待队列中waitStatus不为CONDITION的节点,最后根据interruptMode抛出对应的异常。
/**
* Unlinks cancelled waiter nodes from condition queue.
* Called only while holding lock. This is called when
* cancellation occurred during condition wait, and upon
* insertion of a new waiter when lastWaiter is seen to have
* been cancelled. This method is needed to avoid garbage
* retention in the absence of signals. So even though it may
* require a full traversal, it comes into play only when
* timeouts or cancellations occur in the absence of
* signals. It traverses all nodes rather than stopping at a
* particular target to unlink all pointers to garbage nodes
* without requiring many re-traversals during cancellation
* storms.
*/
private void unlinkCancelledWaiters() { //清除condition队列中节点ws不为condition的节点
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
await()思维导图
仅供参考
signal()和signalAll()
我们已经了解了如何将线程置为等待,现在来看看怎么唤醒线程,await()对应的唤醒线程有两种方式,signal()和signalAll(),从字面意思上我们就可以知道signalAll()就是唤醒所有的线程,唤醒所有线程会将condition等待队列上的所有线程都加入到同步队列中。
/**
* Moves the longest-waiting thread, if one exists, from the
* wait queue for this condition to the wait queue for the
* owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signal() {
if (!isHeldExclusively()) //锁不被当前线程持有
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
/**
* Moves all threads from the wait queue for this condition to
* the wait queue for the owning lock.
*
* @throws IllegalMonitorStateException if {@link #isHeldExclusively}
* returns {@code false}
*/
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
我们从两者的代码中发现,在进行唤醒之前,都会先调用isHeldExcusively()方法先判断,就是先判断锁是否被当前线程占有。
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
现在来分析doSignal()和doSignalAll()的差异
/**
* 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) && //cas设置ws从condition为0失败(可能ws!=condition)并且该节点的下一个节点不为空
(first = firstWaiter) != null); //如果cas成功就执行一次,失败才循环
}
/**
* 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()中只有当transferForSignal()失败并且condition等待队列中下一个节点不为空时对下一个节点调用transferForSignal(),也就是说当transferForSignal()成功或者节点为空就会退出循环。而signalAll()是如果节点不为空就依次对队列中的所有节点调用transferForSignal(),下面来看看transferForSignal()方法
/**
* Transfers a node from a condition queue onto sync queue.
* Returns true if successful.
* @param node the node
* @return true if successfully transferred (else the node was
* cancelled before signal)
*/
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) //CAS自旋设置CONDITION为0,若失败,返回false
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).
*/
Node p = enq(node); //成功就加入aqs队列,返回true
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) //前一个节点的ws为canceled或者cas设置为signal失败,释放线程
LockSupport.unpark(node.thread);
return true;
}
transferForSignal()方法中先cas尝试设置waitStatus从CONDITION为0,如果失败(可能这个节点的waitStatus不为CONDITION)则返回false,成功则将节点加入同步队列,加入同步队列后,这个节点在同步队列中的前一个节点的waitStatus如果为CANCELLED或者cas设置SIGNAL失败则unpark唤醒线程,以重新加入同步队列。