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

< 0 // 此时 h 是之前的头结点|| (h = head) == null || h.waitStatus < 0) { // 此时 h 已经更新为当前头结点Node s = node.next;// 如果后继结点以共享模式在等待 , 或者后继结点未知 , 则尝试唤醒后继结点if (s == null || s.isShared()) {this.doReleaseShared();}}}因为当前结点已经获取到资源 , 所以需要将当前结点记录到头结点中 。 此外 , 如果满足以下 2 种情况之一 , 还需要唤醒后继结点:

  1. 参数 propagate > 0 , 即存在可用的剩余资源;
  2. 前任头结点或当前头结点不存在 , 或指明后继结点需要被唤醒 。
如果满足上述条件之一 , 且后继结点状态未知或以共享模式在等待 , 则调用 AbstractQueuedSynchronizer#doReleaseShared 方法唤醒后继结点 , 关于该方法的实现留到下一小节进行分析 。
共享释放资源针对共享模式释放资源 , AbstractQueuedSynchronizer 同样定义了单一实现 , 即 AbstractQueuedSynchronizer#releaseShared 方法 , 该方法本质上也是一个调度的过程 , 具体释放资源的操作交由 tryReleaseShared 方法完成 , 由子类实现 。 方法 AbstractQueuedSynchronizer#releaseShared 实现如下:
public final boolean releaseShared(int arg) {// 尝试释放资源if (this.tryReleaseShared(arg)) {// 释放资源成功 , 唤醒后继结点this.doReleaseShared();return true;}return false;}private void doReleaseShared() {/** Ensure that a release propagates, even if there are other in-progress acquires/releases.* This proceeds in the usual way of trying to unparkSuccessor of head if it needs signal.* But if it does not, status is set to PROPAGATE to ensure that upon release, propagation continues.* Additionally, we must loop in case a new node is added while we are doing this.* Also, unlike other uses of unparkSuccessor, we need to know if CAS to reset status fails, if so rechecking.*/for (; ; ) {Node h = head;if (h != null// 如果头结点状态为 SIGNAL , 则在唤醒后继结点之前尝试清除当前结点的状态if (ws == Node.SIGNAL) {if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) {// loop to recheck casescontinue;}// 唤醒后继结点this.unparkSuccessor(h);}/** 如果后继结点暂时不需要被唤醒 , 则基于 CAS 尝试将目标结点的 waitStatus 由 0 修改为 PROPAGATE ,* 以保证后续由唤醒通知到来时 , 能够将通知传递下去*/else if (ws == 0}}// 如果头结点未变更 , 则说明期间持有锁的线程未发生变化 , 能够走到这一步说明前面的操作已经成功完成if (h == head) {break;}// 如果头结点发生变更 , 则说明期间持有锁的线程发生了变化 , 需要重试以保证唤醒动作的成功执行}}如果释放资源成功 , 需要依据头结点当下等待状态分别处理:
  1. 如果头结点的等待状态为 SIGNAL , 则表明后继结点需要被唤醒 , 在执行唤醒操作之前需要清除等待状态 。
  2. 如果头结点状态为 0 , 则表示后继结点不需要被唤醒 , 此时需要将结点状态修改为 PROPAGATE , 以保证后续接收到唤醒通知时能够将通知传递下去 。
总结本文我们分析了 AQS 的设计与实现 。 理解了 AQS 的运行机制也就理解了 java 的 Lock 锁是如何实现线程的阻塞、唤醒、等待和通知机制的 , 所以理解 AQS 也是我们后面分析 Lock 锁和同步器实现的基础 。
【深入理解 JUC:AQS 队列同步器】从下一篇开始 , 我们将介绍 JUC 中基于 AQS 实现的组件 , 包括 ReentrantLock、ReentrantReadWriteLock、CountDownLatch , 以及 Semaphore 等 , 去分析 AQS 中定义的模板方法是如何在这些组件中进行实现的 。


推荐阅读