AQS的release方法先调用tryRelease释放状态,成功后才通过unparkSuccessor唤醒头节点的有效后继;unparkSuccessor从head.next开始正向查找首个非取消节点并唤醒其线程,若失败则反向扫描确保不遗漏,唤醒后线程需重新竞争锁。

Java中 AQS 的 release 方法本身并不直接调用 unparkSuccessor,而是先尝试释放同步状态(通过 tryRelease),成功后才唤醒等待队列中的后继节点——这个唤醒动作正是由 unparkSuccessor 完成的。
release 流程中 unparkSuccessor 的触发时机
当线程调用 release(int arg) 时,AQS 执行以下关键步骤:
- 调用子类实现的
tryRelease(arg),尝试释放锁或同步状态; - 若
tryRelease返回true(表示完全释放,例如独占锁中 state 归零),则继续执行唤醒逻辑; - 获取头节点(
head),检查其后继节点是否有效(非 null、非取消状态); - 若后继节点存在且可唤醒,则调用
unparkSuccessor(h)唤醒它。
unparkSuccessor 怎么找到并唤醒后继节点
unparkSuccessor(Node node) 接收的是当前头节点(即刚释放锁的节点),它的核心逻辑是:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 先尝试将该头节点的
waitStatus重置为 0(避免虚假唤醒干扰); - 从头节点的
next开始向后遍历,跳过所有waitStatus > 0(即已取消)的节点; - 找到第一个有效的后继节点后,调用
LockSupport.unpark(s.thread)唤醒其关联线程; - 如果
next为 null 或已取消,会退回到从队列尾部反向扫描(防止并发入队导致 next 滞后),确保不遗漏真正需要唤醒的节点。
为什么唤醒的是“后继”而不是“下一个排队最久的”
AQS 的 CLH 变种队列是“虚节点头”,头节点本身是已释放/已完成的线程(比如刚释放锁的线程),它不参与等待。真正的等待者从 head.next 开始。所以:
立即学习“Java免费学习笔记(深入)”;
-
head是“哨兵”,代表前一个获得并释放了同步状态的线程; -
head.next才是下一个应被唤醒去竞争锁的线程(即等待最久的有效节点); -
unparkSuccessor的目标就是把这个“最靠前的有效后继”唤醒,让它回到acquireQueued中尝试tryAcquire。
注意:唤醒不等于立即获得锁
unparkSuccessor 只负责唤醒线程,该线程被唤醒后需重新竞争同步状态:
- 被唤醒线程会回到
acquireQueued的自旋中,再次调用tryAcquire; - 若此时同步状态可用(如锁未被占用),则成功获取并成为新的 head;
- 若仍不可用,它可能再次挂起(取决于是否继续自旋或阻塞),但已脱离“纯等待”状态。

















