AQS的Condition是基于Lock实现的线程等待通知组件,每个Condition实例都关联一个独立的等待队列,线程调用await方法后会进入该队列等待,被signal唤醒后需要转移到AQS的同步队列中重新竞争锁,整个过程需要保证操作的原子性,避免线程状态错乱。

Condition队列的基本结构
Condition队列是AQS内部维护的一个单向链表,每个节点复用了AQS同步队列的Node类,不过Condition队列的节点只有nextWaiter指针,没有prev和next指针,队列的头节点是firstWaiter,尾节点是lastWaiter。线程调用lock.newCondition()创建的Condition实例,内部持有这个队列的引用。
线程进入等待队列的流程
当线程持有锁的情况下调用Condition的await方法时,首先会将当前线程封装成Node节点加入Condition队列的尾部,然后释放当前持有的锁,之后循环检查自己是否在同步队列中,如果不在就挂起线程。
以下是await方法的核心源码逻辑:
public final void await() throws InterruptedException {
// 响应中断检查
if (Thread.interrupted())
throw new InterruptedException();
// 将当前线程加入Condition等待队列
Node node = addConditionWaiter();
// 释放当前持有的锁,返回释放前的锁状态
int savedState = fullyRelease(node);
int interruptMode = 0;
// 循环检查节点是否在同步队列中,不在就挂起
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 进入同步队列后竞争锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 清理取消的节点
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
// 添加节点到Condition队列尾部
private Node addConditionWaiter() {
Node t = lastWaiter;
// 如果尾节点被取消,清理所有取消的节点
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
// 创建新的等待节点,状态为CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
线程被唤醒后转移到同步队列的原子过程
当其他线程调用Condition的signal方法时,会将Condition队列的头节点转移到同步队列中,这个转移过程需要保证原子性,避免多个线程同时操作节点导致状态不一致。
signal方法的核心逻辑是先获取Condition队列的头节点,然后调用transferForSignal方法完成转移,以下是相关源码:
public final void signal() {
// 检查当前线程是否持有锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
// 移除头节点,将firstWaiter指向下一个节点
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
// 将节点从Condition队列转移到同步队列,返回是否成功
final boolean transferForSignal(Node node) {
// 原子修改节点状态,从CONDITION改为0,失败说明节点已经被取消
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将节点加入同步队列尾部,返回前驱节点
Node p = enq(node);
int ws = p.waitStatus;
// 如果前驱节点被取消,或者修改前驱节点状态为SIGNAL失败,就唤醒当前节点线程
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
transferForSignal方法是整个转移过程的核心,首先通过CAS操作将节点的waitStatus从CONDITION改为0,这一步是原子操作,保证只有被唤醒的节点能完成状态变更。如果CAS失败,说明节点已经被取消,直接返回false,doSignal会继续处理下一个节点。CAS成功后,调用enq方法将节点加入同步队列的尾部,enq方法内部也是通过自旋加CAS的方式保证入队操作的原子性。最后修改前驱节点的状态为SIGNAL,确保前驱节点释放锁时能唤醒当前节点,如果修改失败就直接唤醒当前节点,避免线程一直挂起。
转移过程的原子性保证
整个转移过程的原子性主要通过两点实现:一是节点状态变更的CAS操作,保证同一个节点不会被多次转移;二是同步队列入队的enq方法通过自旋CAS保证入队操作的原子性,避免多个线程同时入队导致队列结构错乱。同时signal方法要求调用线程必须持有锁,保证了同一时间只有一个线程执行唤醒操作,进一步避免了并发问题。
特殊情况处理
如果线程在等待过程中被中断,await方法会先检查中断状态,若中断发生在signal之前,会将中断模式设置为THROW_IE,之后转移节点到同步队列后抛出中断异常;若中断发生在signal之后,会将中断模式设置为REINTERRUPT,之后重新设置中断标志。如果节点在转移前被取消,CAS修改状态会失败,该节点不会被转移到同步队列,后续会被清理出Condition队列。
AQSCondition_队列线程等待同步队列原子过程修改时间:2026-06-10 06:21:30