PROPAGATE不是AQS的属性,而是setHeadAndPropagate方法的入参,表示获取后剩余资源数:propagate>0时立即唤醒后继线程;=0时资源耗尽,不主动传播但允许其他机制兜底。

propagate 不是 AQS 的一个“属性”,而是 setHeadAndPropagate 方法的入参,代表当前获取操作后**是否还有剩余资源可供后续线程继续获取**。它本身不存储在 Node 或 AQS 实例中,但它的值会直接影响是否触发唤醒传播逻辑。
propagate 决定要不要继续叫醒后面的人
共享模式下,一个线程成功获取资源(比如拿到一个 Semaphore 许可),不代表队列就该停了——可能还有剩余许可,后面线程也能立刻拿。propagate 就是这个“还能不能接着放行”的信号:
- propagate > 0:我拿到了,而且资源还有剩 → 立刻调用 doReleaseShared 尝试唤醒后继
- propagate == 0:我拿到了,但刚好用完 → 不主动传播,但不等于“禁止唤醒”,后续靠其他机制兜底
- propagate
仅靠 propagate 不够,PROPAGATE 状态才是关键补丁
实际场景中,多个线程可能并发 release 和 acquire。比如:
- T1 刚把 head 设为自己的节点,propagate = 0(许可刚好耗尽)
- T2 同时 release 一个许可,更新 state,但还没来得及唤醒任何人
这时 Node.waitStatus == PROPAGATE(-3) 就起作用了:它是一个“中间态标记”,表示“刚才有释放发生,唤醒应该继续传下去”。哪怕 propagate 是 0,只要旧 head 或新 head 的 waitStatus 是 PROPAGATE 或 SIGNAL,setHeadAndPropagate 仍会触发 doReleaseShared,确保 T2 的释放不被忽略。
立即学习“Java免费学习笔记(深入)”;
唤醒传播不是“唤醒一个”,而是“唤醒所有可能能拿的”
doReleaseShared 的核心行为是循环尝试:
- 检查当前 head 是否有后继
- 如果后继是共享节点(isShared()),且状态允许(非 CANCELLED),就 unpark 它
- 然后把 head 的 waitStatus 设为 PROPAGATE,为下一轮传播铺路
- 这个过程可能反复多次,直到队列里再没有连续的共享节点可唤醒,或资源再次耗尽
这保证了 Semaphore 从 0→3 时,能一口气唤醒最多 3 个等待线程;CountDownLatch 倒计数归零时,能一次性放开全部等待者。
没有 PROPAGATE 就容易卡死
早期 JDK 版本没引入 PROPAGATE,只依赖 tryAcquireShared 返回值和 SIGNAL 状态。结果是:
- 并发 release 后,新 head 的 waitStatus 可能被设为 0
- 后续 acquire 线程看到 propagate == 0 + waitStatus == 0,就不再传播
- 即使资源已恢复,队列后半段线程仍 park 着,造成假死
PROPAGATE 就是为解决这个竞态而加的“保险机制”——它让唤醒信号具备传染性,不依赖单一返回值,也不怕中间状态丢失。


















