1. AQS 共享模式中的传播机制如何支持 Semaphore 和 CountDownLatch,何时会连续唤醒后继节点
AQS 的共享模式(shared mode)中,当 tryAcquireShared 返回非负值时,releaseShared 会触发对后继节点的传播唤醒(propagation),这种连续的唤醒机制如何让 Semaphore 和 CountDownLatch 得以工作?
- AQS 共享模式与独占模式(setHead 与 setHeadAndPropagate)的区别
- doReleaseShared 的传播链与短路条件
- Semaphore 靠可重复获取许可、CountDownLatch 靠一次性归零的实现差异
共享模式下,允许多个线程同时获取"许可",这正是 Semaphore 与 CountDownLatch 的基础。当 releaseShared 成功释放后,AQS 调用 doReleaseShared,将 head 的状态从 SIGNAL 改为 0 并 unpark 后继节点;被唤醒的线程在 acquireShared 中成功拿到共享许可后,若还有剩余资格(tryAcquireShared 返回值仍是正数),会通过 setHeadAndPropagate 继续唤醒后继,形成"传播链"。对 Semaphore 而言,每次 release 可能让多个线程依次获得许可,因此需要连续唤醒;对 CountDownLatch 而言,count 归零后所有等待线程共享"已经打开"的信号,setHeadAndPropagate 会唤醒所有排队节点。传播链的短路条件是发现 head 已经处理过(head 状态为 0 或 head 未变化),避免无限次唤醒。
关键在 setHeadAndPropagate 里的 propagate 判断:仅当共享许可仍有剩余(tryAcquireShared 返回值 > 0)或 head 需要唤醒时才继续,否则停止,这正是"何时连续唤醒后继节点"的答案。相比独占模式,共享模式把"释放一个唤醒多个"的语义显式转成"唤醒一个、成功者再唤醒下一个"的接力,从而在极高唤醒下也能控制唤醒次数。
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
} else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head) break;
}
}