sethead方法仅执行简单赋值并清空thread和prev引用,不提供线程安全机制;其安全性依赖于调用前提——必须在tryacquire成功、当前线程已独占获取锁且满足p==head条件下由单一线程执行,配合volatile head字段保证可见性与有序性。

在 AQS(AbstractQueuedSynchronizer)中,setHead 方法本身不负责“安全设置”头节点,它只是一个简单的赋值操作;真正的线程安全,依赖于调用它的上下文——即必须在已获得同步状态(锁)的前提下、且由当前独占线程执行,并配合 CAS 和 volatile 语义来保证可见性与原子性。
setHead 只是“清理式赋值”,不带同步逻辑
setHead 方法定义非常简单:
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
它不做任何 CAS 或 volatile 写入(head 字段本身是 volatile),也不检查当前线程是否合法。它的作用仅是:把刚获取到锁的节点设为新头,并清空其 thread 和 prev 引用(表示该节点已脱离等待队列,成为“虚头”)。安全性来自外部约束:
- 该方法只在成功获取锁后被调用(如
acquireQueued中的if (p == head && tryAcquire(arg))分支) - 此时当前线程已通过
tryAcquire独占获取同步状态,整个临界区逻辑由 AQS 的 acquire 流程串行保障 -
head是volatile字段,写入立即对其他线程可见,避免重排序
真正关键的是“谁在什么时候调用”
头节点更新不是并发竞争操作,而是单线程推进行为:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 只有前驱节点是当前头节点(
p == head)且当前线程成功tryAcquire时,才进入setHead - 这个条件天然排除了多个线程同时设置头节点的可能(因为锁未释放前,其他线程无法通过
tryAcquire) - 即使多个线程几乎同时唤醒,AQS 的 FIFO 队列结构 + 前驱判断确保只有一个线程满足
p == head并能完成获取
为什么不需要 CAS?
setHead 不用 CAS,是因为:
- 头节点变更发生在“已成功获取锁”的瞬间,此时没有其他线程能并发修改
head(因为锁未释放,后续线程还在阻塞或自旋中) - 即使发生虚假唤醒,未满足
p == head的线程不会执行setHead - CAS 在这里既无必要(无竞争),也无意义(没有失败重试逻辑)
注意:head 的 volatile 语义不可替代
虽然 setHead 是普通赋值,但 head 字段声明为 volatile:
private transient volatile Node head;
这确保了:
- 新 head 的写入对所有线程立即可见(例如后续唤醒 successor 时能正确读到新 head)
- 禁止编译器和 CPU 将
head = node与前面的setState(1)等操作重排序 - 构成“happens-before”关系,使锁获取的内存效果对其他线程生效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










