深入理解 JUC:AQS 队列同步器( 六 )


方法 ConditionObject#signal 的实现如下:
public final void signal() {// 先检测当前线程是否获取到了锁 , 否则不允许继续执行if (!isHeldExclusively()) {throw new IllegalMonitorStateException();}// 获取条件队列头结点 , 即等待时间最长的结点Node first = firstWaiter;if (first != null) {// 将头结点从条件队列转移到同步队列 , 参与竞争资源this.doSignal(first);}}调用 ConditionObject#signal 方法的线程必须位于临界区 , 也就是必须先持有独占锁 , 所以上述方法一开始会对这一条件进行校验 , 方法 AbstractQueuedSynchronizer#isHeldExclusively 是一个模板方法 , 交由子类来实现 。 如果满足执行条件 , 则上述方法会调用 ConditionObject#doSignal 方法将条件队列的头结点从条件队列转移到同步队列 。
private void doSignal(Node first) {// 从前往后遍历 , 直到遇到第一个不为 null 的结点 , 并将其从条件队列转移到同步队列do {if ((firstWaiter = first.nextWaiter) == null) {lastWaiter = null;}first.nextWaiter = null;} while (!transferForSignal(first) }// AbstractQueuedSynchronizer#transferForSignalfinal boolean transferForSignal(Node node) {// 更新当前结点的等待状态:CONDITION -> 0if (!compareAndSetWaitStatus(node, 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).*/// 将结点添加到同步队列末端 , 并返回该结点的前驱结点Node p = this.enq(node);int ws = p.waitStatus;// 如果前驱结点被取消 , 或者设置前驱结点的状态为 SIGNAL 失败 , 则唤醒当前结点上的线程if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) {LockSupport.unpark(node.thread);}return true;}方法 ConditionObject#doSignal 会从前往后遍历条件队列 , 寻找第一个不为 null 的结点 , 并应用 AbstractQueuedSynchronizer#transferForSignal 方法尝试将其从条件队列转移到同步队列 。
在入同步队列之前 , 方法 AbstractQueuedSynchronizer#transferForSignal 会基于 CAS 机制清除结点的 CONDITION 状态 , 如果清除失败则说明该结点上的线程已被取消 , 此时 ConditionObject#doSignal 方法会继续寻找下一个可以被唤醒的结点 。 如果清除结点状态成功 , 则接下来会将该结点添加到同步队列的末端 , 同时依据前驱结点的状态决定是否唤醒当前结点上的线程 。
继续来看 ConditionObject#signalAll 方法的实现 , 相对于上面介绍的 ConditionObject#signal 方法 , 该方法的特点在于它会唤醒条件队列中所有不为 null 的等待结点 。 方法实现如下:
public final void signalAll() {if (!isHeldExclusively()) {// 先检测当前线程是否获取到了锁 , 否则不允许继续执行throw new IllegalMonitorStateException();}// 获取条件队列头结点Node first = firstWaiter;if (first != null) {// 将所有结点从条件队列转移到同步队列 , 参与竞争资源this.doSignalAll(first);}}private void doSignalAll(Node first) {lastWaiter = firstWaiter = null;do {Node next = first.nextWaiter;first.nextWaiter = null;transferForSignal(first);first = next;} while (first != null);}实际上理解了 ConditionObject#doSignal 的运行机制 , 再理解 ConditionObject#signalAll 的运行机制也是水到渠成的事情 。


推荐阅读