Condition等待队列与AQS同步队列是独立且结构不同的链表,节点通过修改指针和状态字段在两者间重挂载而非转移:前者为单向链表仅用nextWaiter,后者为双向链表依赖prev/next;await()释放锁后将线程封装为新Node加入Condition队列并阻塞,signal()则摘下首个CONDITION节点、清空nextWaiter、重置状态后以enq()插入同步队列尾部,唤醒后重新竞争锁。

Condition 的等待队列和 AQS 的同步队列是两个独立但协同工作的队列,节点不会“转移”,而是通过 await() 和 signal() 操作,在不同队列间**重新链接节点指针**,实现线程状态切换与调度协作。
Condition 等待队列是单向链表,与 AQS 同步队列结构不同
ConditionObject 内部维护一个 FIFO 单向等待队列(firstWaiter → lastWaiter),节点类型为 Node,但只使用 nextWaiter 指针(不使用 prev/next)。而 AQS 同步队列是双向链表,依赖 prev、next 和 thread 字段管理阻塞线程。两者共用 Node 类,但语义和链表结构完全不同。
- 等待队列节点在
await()时被创建并加入 Condition 队列,同时从同步队列中“移除”——实际是将当前线程对应的同步队列节点设为CONDITION状态,并断开其在同步队列中的双向链接 -
signal()不会把节点从等待队列“搬”到同步队列,而是将目标等待节点的nextWaiter清空,并将其thread和waitStatus重置后,通过enq()插入同步队列尾部 - 整个过程没有内存拷贝或节点复用,只是修改指针和状态字段,节点对象本身仍为同一个
Node实例
await():释放锁 + 入等待队列 + 阻塞
调用 await() 时,线程需已持有对应 Lock 的独占锁。流程本质是:先完全释放锁(触发 AQS tryRelease()),再把自己封装成新 Node 加入 Condition 等待队列,最后调用 LockSupport.park() 阻塞。
- 释放锁后,若同步队列中有其他线程在等待,可能被唤醒去竞争锁;此时当前线程已脱离同步队列,不再参与锁竞争
- 新建的等待节点仅挂入 Condition 队列,
thread字段保存当前线程引用,waitStatus = Node.CONDITION - 注意:await 过程中若发生中断,会带着中断标志退出等待队列,并尝试重新获取锁(可能阻塞在同步队列中)
signal():唤醒一个等待节点,移交至同步队列
signal() 从 Condition 队列头开始遍历,找到第一个 waitStatus == Node.CONDITION 的有效节点,将其从等待队列摘下,并插入 AQS 同步队列尾部,随后唤醒该线程。
立即学习“Java免费学习笔记(深入)”;
- 摘除操作只修改
firstWaiter或中间节点的nextWaiter指针,不涉及同步队列指针 - 插入同步队列调用的是 AQS 的
enq(node),该方法确保节点以 CAS 方式安全入队,并初始化prev/next,设置waitStatus = 0 - 被 signal 的线程被
LockSupport.unpark()唤醒后,会在await()的后续逻辑中重新尝试获取锁(即进入 acquire 流程),此时它已在同步队列中排队
关键细节:节点不是“转移”,而是“重挂载”
所谓“节点转移”是一种简化说法。实际上,同一个 Node 实例在生命周期中可能先后处于三种状态:
- 在同步队列中:作为竞争锁的排队者,
waitStatus通常为 0 或SIGNAL - 在等待队列中:作为条件等待者,
waitStatus == CONDITION,仅用nextWaiter - 刚被 signal 后、尚未被唤醒前:已从等待队列摘下,正准备或已经插入同步队列,
waitStatus被重置为 0,nextWaiter = null
整个过程不创建新节点,也不销毁旧节点,只是改变节点的链表归属和状态字段,由不同队列的 head/tail 引用和节点自身指针共同维护逻辑位置。


















