ReentrantLock 条件队列 AQS Condition 源码深度解析
在并发编程领域,当我们掌握了互斥锁和共享限流技术后,线程间的协调与通信机制便成为必须深入理解的核心课题。无论是经典的生产者-消费者模式,还是需要严格顺序执行的复杂业务场景,都离不开一套高效、可靠的线程等待与唤醒工具。Java原生的Object.wait()和notify()方法虽然基础,但其单一条件队列、无法定向唤醒、易发生虚假唤醒等局限性,在高并发环境下往往难以满足性能与可靠性的双重需求。
JUC(java.util.concurrent)包中的Condition接口,正是为弥补这些缺陷而设计的强大工具。作为ReentrantLock的黄金搭档,它基于AQS(AbstractQueuedSynchronizer)框架实现,提供了多条件、可精准控制的等待唤醒能力,堪称AQS四大核心组件(同步队列、独占模式、共享模式、条件队列)中最后一块,也是设计最为精妙的拼图。

一、核心价值:Condition 解决了哪些关键痛点?
要深刻理解Condition的价值,首先需要认清原生机制的不足。想象一下,所有需要等待的线程都被迫挤在同一个监视器队列中,唤醒时只能“一视同仁”,无法针对性地唤醒生产者或消费者线程,这不仅效率低下,也极易引发逻辑错误。
Condition的核心贡献在于,它为AQS引入了独立于主同步队列之外的“条件等待队列”。当线程因特定业务条件不满足时,可以主动释放锁并进入指定的条件队列中等待;一旦条件成熟,又能被精准地唤醒,并重新加入同步队列竞争锁资源。这种设计将线程调度的粒度从粗放的“锁级别”细化到了精细的“条件级别”,是并发控制能力的一次重要飞跃。
二、结构基础:AQS 条件队列的底层设计
Condition的核心实现是AQS的内部类ConditionObject。其设计极为巧妙,完全复用了AQS的节点(Node)结构和LockSupport的阻塞唤醒机制,没有引入任何额外的设计负担。
public abstract class AbstractQueuedSynchronizer {
// Condition 核心实现类
public class ConditionObject implements Condition, java.io.Serializable {
// 条件队列:采用单向链表结构(与同步队列的双向链表相区别)
private transient Node firstWaiter;
private transient Node lastWaiter;
public ConditionObject() {}
}
// AQS 同步队列节点(被条件队列复用)
static final class Node {
// 节点状态:CONDITION 表示该节点位于条件队列中
static final int CONDITION = -2;
// 指向条件队列中的下一个等待节点
Node nextWaiter;
}
}
这里有几点关键设计值得深入品味:
- 一锁多条件:一个
ReentrantLock锁实例可以创建多个Condition对象,每个Condition都维护着自己独立的单向链表作为条件队列。 - 状态标识:通过将节点的
waitStatus设置为CONDITION,来明确标记该节点正位于条件队列中,从而与同步队列中的节点状态清晰区分。 - 流程闭环:线程成功获取锁 → 判断业务条件不满足 → 完全释放锁 → 进入指定条件队列等待 → 被
signal唤醒 → 节点转移回主同步队列 → 重新竞争锁 → 继续执行后续逻辑。整个过程形成了一个安全、高效且无死锁风险的完整闭环。
三、源码深度剖析:Condition 核心方法解析
理解了整体骨架后,我们再深入其内部,聚焦三个最核心的方法:await()(线程等待)、signal()(唤醒单个线程)和signalAll()(唤醒所有线程)。
1. 等待核心:await() 方法源码解读
await()是线程进入等待状态的核心入口,其逻辑环环相扣,严谨细致:
public final void await() throws InterruptedException {
// 1. 响应中断:若当前线程已被中断,则直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 2. 将当前线程封装为 CONDITION 状态节点,并添加到条件队列尾部
Node node = addConditionWaiter();
// 3. 完全释放当前线程持有的锁(防止死锁),并保存释放前的锁状态
long sa vedState = fullyRelease(node);
boolean interrupted = false;
// 4. 自旋检查:判断节点是否已被转移到同步队列,若未转移则持续阻塞
while (!isOnSyncQueue(node)) {
// 4.1 使用 LockSupport.park() 阻塞当前线程
LockSupport.park(this);
// 4.2 线程被唤醒后,检查等待期间是否发生中断
if ((interrupted = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 5. 节点已被转移到同步队列,线程被唤醒,重新以独占模式参与锁竞争
if (acquireQueued(node, sa vedState) && interrupted != THROW_IE)
interrupted = REINTERRUPT;
// 6. 清理条件队列中已被取消的等待节点
if (node.nextWaiter != null)
unlinkCancelledWaiters();
// 7. 根据中断策略,抛出异常或重新设置中断标志
if (interrupted != 0)
reportInterruptAfterWait(interrupted);
}
其中,addConditionWaiter()方法负责创建并加入条件队列节点。这里有一个至关重要的细节:await()方法必须在当前线程已经持有与该Condition关联的锁的情况下调用,否则后续的fullyRelease()调用将抛出IllegalMonitorStateException异常。调用await()会完全释放锁,这正是为了确保其他线程能够获取锁来修改条件变量。线程在阻塞期间仅存在于条件队列,不占用任何锁资源,从而避免了死锁。
2. 唤醒核心:signal() 方法源码解读
唤醒操作的核心逻辑是将符合条件的节点从条件队列“迁移”到主同步队列。
public final void signal() {
// 1. 前置校验:调用线程必须持有与此Condition关联的独占锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
// 2. 唤醒条件队列中的第一个有效等待节点
if (first != null)
doSignal(first);
}
// 辅助方法:执行具体的唤醒(节点转移)操作
private void doSignal(Node first) {
do {
// 将条件队列的头节点指针后移
if ((firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
// 3. 尝试将节点从条件队列转移到同步队列,若失败则继续尝试下一个节点
} while (!transferForSignal(first) && (first = firstWaiter) != null);
}
// 核心转移方法:将节点从条件队列移动到同步队列
final boolean transferForSignal(Node node) {
// 1. 使用CAS操作将节点状态从 CONDITION 更新为 0(初始状态)
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false; // 转移失败,可能节点已被取消
// 2. 将节点加入AQS同步队列的尾部,并返回其前驱节点
Node p = enq(node);
int ws = p.waitStatus;
// 3. 若前驱节点状态为取消,或无法将其状态设置为SIGNAL,则立即唤醒线程
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
3. 全量唤醒:signalAll() 方法源码解读
signalAll()的逻辑与signal()类似,区别在于它会遍历整个条件队列,将所有状态有效的等待节点都转移到同步队列。
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
// 遍历条件队列,转移所有节点到同步队列
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
必须明确一个关键机制:signal()或signalAll()仅仅完成了节点的转移和线程的唤醒(通过LockSupport.unpark)。被唤醒的线程并不会立即恢复执行,而是需要重新进入同步队列,遵循AQS的规则去竞争锁,成功获取锁之后才能从await()方法中返回。这保证了条件判断和后续操作的线程安全性与原子性。
四、实践结合:ReentrantLock 中的 Condition 实现
在实际开发中,我们通常通过ReentrantLock来获取Condition实例。其实现简洁而高效,直接复用了AQS内部的ConditionObject。
public class ReentrantLock implements Lock {
private final Sync sync;
// 创建并返回一个绑定到当前锁的 Condition 实例
public Condition newCondition() {
return sync.newCondition();
}
abstract static class Sync extends AbstractQueuedSynchronizer {
final ConditionObject newCondition() {
return new ConditionObject();
}
}
}
五、设计思想与工程启示
初看Condition,或许会认为它仅仅是wait/notify的一个“增强版”。但深入其设计哲学,你会发现这背后体现了AQS对“线程同步粒度”的极致追求。原生的wait/notify机制将所有等待线程置于同一个“篮子”里,唤醒时难免“误伤友军”,造成不必要的竞争。而Condition允许我们根据不同的业务条件(例如“缓冲区空”和“缓冲区满”)将等待线程分组到不同的队列中,实现精准的、按需的线程调度。
这种“分而治之”、“精准调度”的设计思想,其价值远超线程同步本身。它在高并发任务调度、消息队列的路由策略、事件驱动架构等领域都有深刻的体现,是构建高性能、高可控性分布式系统的重要思维模型和底层支撑。
六、应用场景与实战指南
1. 核心应用场景
- 生产者-消费者模型:创建两个
Condition实例,分别用于生产者在队列满时等待,消费者在队列空时等待,实现精准的定向通知,极大提升效率。 - 多线程顺序控制:严格控制线程A、B、C必须按预定顺序执行,每个线程执行完毕后精准唤醒下一个线程,避免无效的全局唤醒带来的性能损耗。
- 自定义同步组件:基于AQS和Condition实现带有复杂业务条件的同步器,例如支持超时获取、可中断、带权限校验的高级锁。
- 池化资源管理:数据库连接池、线程池等在资源耗尽时,让请求线程在特定的条件队列中等待,当有资源释放时再精准唤醒等待线程,优化资源利用率。
2. 实战避坑指南(高频错误)
- 坑点1:未持有锁时调用 await()/signal():这是最常见的运行时错误,会直接抛出
IllegalMonitorStateException。务必确保在lock.lock()和lock.unlock()构成的临界区内调用这些方法。 - 坑点2:忽略 await() 的中断处理:如果业务逻辑不关心线程中断,建议使用
awaitUninterruptibly()方法。若使用可中断的await(),必须妥善捕获并处理InterruptedException,避免线程意外退出导致程序状态不一致。 - 坑点3:混淆 signal() 与 signalAll() 的使用场景:只需唤醒一个等待线程来处理条件变化时,使用
signal();需要唤醒所有等待同一条件的线程时(例如资源释放),使用signalAll()。错误使用可能导致线程饥饿或永久等待。 - 坑点4:修改条件变量后忘记唤醒:这是一个逻辑错误。在修改了使条件成立的状态变量后,必须记得调用对应的
signal()或signalAll()方法,否则条件队列中的线程将永远无法被唤醒,造成“线程泄露”。
3. 实战标准模板(生产者-消费者模型)
以下是一个经典的生产者-消费者模型实现,清晰展示了双Condition的协同工作方式:
ReentrantLock lock = new ReentrantLock();
// 队列空条件:消费者线程在此条件上等待
Condition emptyCond = lock.newCondition();
// 队列满条件:生产者线程在此条件上等待
Condition fullCond = lock.newCondition();
Queue
七、核心总结
总而言之,Condition是JUC提供的一套工业级、精准化的线程等待唤醒机制。它基于AQS独立的条件队列实现,核心流程设计严谨。必须与ReentrantLock(或其他实现了Lock接口的锁)配合使用,且所有await和signal操作都必须在持有锁的上下文中进行。相较于原始的wait/notify机制,它在支持多条件队列、实现精准唤醒、提供灵活的中断处理策略等方面具有压倒性优势,是现代Java高并发编程中处理复杂线程间协作的首选工具。深入理解其原理并掌握最佳实践,对于构建健壮、高效、可维护的多线程应用程序至关重要。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
海康威视多款产品入选省级推广目录并亮相江苏绿建展
近日,2026年江苏绿色低碳建筑国际博览会在南京国际博览中心拉开帷幕。本届展会以“科技创新赋能城乡高质量发展”为主题,吸引了近300家来自政府机构、设计院所、代表房企、技术企业及城投城建等领域的单位参展。海康威视以“数智引领 智筑未来”为主题亮相,集中展示了其在城市更新、城市生命线以及改善型住宅等住
米家白色钛钢恒温电水壶新品上市 售价159元
小米米家近期在有品平台正式推出了恒温电水壶钛钢版白色款。这款电水壶集大容量、健康材质与智能恒温于一体,1 7升容量搭配食品级316Ti钛钢内胆,并支持55°C长效保温,当前售价仅为159元,性价比突出。 根据官方产品详情,这款电水壶的核心优势在于其内胆材质。它选用了316Ti食品接触级钛钢,这种材质
2026年SaaS行业地理优化服务商权威测评与深度解析
2026年,生成式AI的商业化应用已进入关键爆发期。对于SaaS行业而言,产品推广、客户获取、订阅转化乃至渠道合作,这些核心增长环节正全面向AI应答平台迁移。一个明确的共识是:GEO(全球引擎优化)已不再是锦上添花的“增长选项”,而是决定企业未来市场地位的核心战略资产。 其背后的逻辑清晰可见。传统的
京东方攻克OLED面板裂纹难题 恢复为iPhone 17供货
苹果供应链的版图上,一场关键的“回归”正在上演。据韩媒4月28日报道,中国面板巨头京东方(BOE)已重新获得苹果公司的量产批准,正式为iPhone 17系列生产OLED面板。要知道,去年京东方虽曾拿到“入场券”,却因质量问题中途折戟。此番卷土重来,不仅意味着其成功攻克了此前导致生产中断的技术难题,更
梅雨季除湿机选购指南 德业T22A3是否值得入手
又快到了南方朋友一年一度的“渡劫”时节。回南天的湿气还没散尽,梅雨季的连绵阴雨就已经在路上了。从三四月墙壁“冒汗”,到六七月空气都能拧出水,室内湿度动不动就冲到80%、90%以上。这可不只是体感黏糊糊那么简单——长期处在高湿环境里,健康隐患、衣物霉斑、家具变形等问题会接踵而至。与其年复一年跟潮湿斗智
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

