前驱节点是aqs中决定当前节点能否尝试获取锁的关键判定点,只有prev==head时才允许执行tryacquire;其waitstatus设为signal以触发唤醒,若为cancelled则需跳过并修复链表。

前驱节点在 AQS 结构中不是“辅助角色”,而是决定当前节点能否尝试获取锁的关键判定点。
前驱节点是唤醒和轮询的触发开关
当前线程封装为 Node 加入同步队列后,不会主动抢锁,而是检查自己的 prev(前驱节点) 是否为头节点(head)。只有当前驱是 head 时,才被允许执行一次 tryAcquire 尝试——这是 AQS 公平性与唤醒逻辑的基石。
- 若 prev == head:说明当前节点是队列中“下一个最该被调度”的线程,可尝试获取锁
- 若 prev ≠ head:线程保持阻塞(park),不参与竞争,避免无意义的 CAS 自旋
- 头节点本身不关联具体线程(head.thread == null),它只起占位和调度作用
前驱节点状态直接影响当前线程是否被挂起
当前节点入队后,会通过 CAS 将前驱节点的 waitStatus 设为 SIGNAL(-1),表示“我挂起后,请你释放锁时唤醒我”。这个动作必须成功,否则可能引发唤醒丢失。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 如果设置 SIGNAL 失败(比如前驱已取消),当前节点会向更前的节点传播唤醒信号
- 若前驱节点处于 CANCELLED 状态,当前节点会跳过它,向前寻找第一个非取消节点作为有效前驱
- waitStatus 为 0 的前驱节点无法触发唤醒,当前线程将长期 park,除非被显式 unpark 或中断
前驱节点决定是否需要自旋等待
AQS 不采用 CLH 原始的“自旋查前驱状态”方式,但保留了轻量级轮询机制:当发现前驱是 head 且 state 可用时,会先自旋几次再 park,减少上下文切换开销。
- 自旋仅发生在前驱刚变为 head 的短暂窗口,不是持续轮询
- 自旋次数有限(通常 1~2 次),失败即 park,避免 CPU 空转
- 非公平模式下,即使前驱不是 head,线程仍可能插队 tryAcquire,但一旦失败,仍要按队列顺序依赖前驱
前驱节点取消时的链表修复逻辑
如果前驱节点因超时或中断被设为 CANCELLED(waitStatus = 1),当前节点不能依赖它唤醒,必须绕过并更新前驱引用。
- addWaiter 中会循环跳过所有 CANCELLED 前驱,找到最近的有效前驱再链接
- acquireQueued 中若发现前驱已取消,会将其从队列中“逻辑剔除”,推动 tail 向前收缩
- 这个过程不涉及锁,靠 volatile + CAS 保证可见性与原子性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










