AQS中的CLH变体等待队列是基于Node的虚拟双向链表,非传统Queue;线程获取失败后创建Node并CAS入队尾,waitStatus控制阻塞与精准唤醒,双向结构支持取消、跳过无效节点及稳定调度。

AQS 中的 CLH 变体等待队列不是传统 Queue,而是一个由 Node 构成的虚拟双向链表,核心目标是安全、高效地管理线程阻塞与唤醒过程,不依赖锁,全靠 CAS 和 volatile 语义保障一致性。
节点如何创建并入队
线程调用 acquire() 获取资源失败后,并不会立刻阻塞,而是走标准入队流程:
- 调用
addWaiter(Node.EXCLUSIVE)创建新 Node,封装当前线程和独占模式 - 先尝试 CAS 更新 tail:将新节点的
prev指向原 tail,再用 CAS 把 tail 改为新节点;成功则入队完成 - 若 CAS 失败(说明有并发入队),则退入
enq()自旋重试,确保最终插入队尾 - 新节点的
thread字段保存线程引用,waitStatus初始化为 0,next为 null
waitStatus 如何控制阻塞行为
waitStatus 是决定线程是否该被挂起的关键字段,不是装饰用的标记:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- SIGNAL (-1):表示“我的后继节点需要被唤醒”,前驱节点在释放或准备 park 前会设此值
- CANCELLED (1):线程中断、超时或放弃竞争,该节点退出调度,后续会被跳过或清理
- CONDITION (-2):仅用于 Condition 队列,不在同步队列中生效
- 在
shouldParkAfterFailedAcquire()中,只当前驱waitStatus == SIGNAL时才返回 true 允许 park;否则尝试将其设为 SIGNAL 并重试
唤醒如何精准定位有效后继
唤醒不是广播,而是定向、可跳过的链式通知:
立即学习“Java免费学习笔记(深入)”;
- 释放操作调用
unparkSuccessor(head),从 head 开始向后扫描第一个waitStatus ≤ 0的节点 - 自动跳过所有
CANCELLED节点,避免无效 unpark - 找到后调用
LockSupport.unpark(node.thread),仅唤醒这一个线程 - 被唤醒线程回到
acquireQueued()自旋中,再次调用tryAcquire()竞争 state,不是直接获得锁
为什么必须是双向链表
单向 CLH 锁无法满足实际工程需求,AQS 改为双向结构解决了三个关键问题:
- 支持取消:线程中断时可通过
prev快速定位前驱,修正其next指针,实现 O(1) 删除 - 便于唤醒传播:共享模式下需唤醒多个后继,双向结构允许从 tail 往前遍历或跳过无效节点
- 避免假唤醒:每个 Node 显式持有
thread和waitStatus,状态可验证,不依赖隐式轮询 - head 始终是哨兵节点(thread 为 null),真正等待者是
head.next,这种设计分离了队列管理和业务线程,提升稳定性

















