condition是reentrantlock的配套工具,支持多等待队列和精准唤醒;一个锁可创建多个condition实例,各管理独立fifo队列,需配合lock/unlock使用while循环判断条件,优先用signal()实现高效线程调度。

Condition 是 ReentrantLock 的配套等待/通知工具,能替代 synchronized + wait/notify 的粗粒度唤醒,实现按条件精准唤醒指定线程组。它不依附于对象监视器,而是绑定在锁上,一个 Lock 可创建多个 Condition 实例,每个代表一类等待条件,从而避免“虚假唤醒”和“唤醒错人”问题。
一、Condition 的基本用法与核心契约
使用前必须先获取锁,调用 lock.newCondition() 获取 Condition 对象;所有 await/signal 操作都必须在持有对应锁的前提下执行,否则抛 IllegalMonitorStateException。
- await():释放当前锁,挂起线程,并加入该 Condition 的等待队列;被 signal 后需重新竞争锁,成功获取后才继续执行
- signal():唤醒等待队列中**一个**线程(FIFO);若队列为空则无效果
- signalAll():唤醒该 Condition 上所有等待线程
- 注意:signal 不会释放锁,唤醒后线程仍需抢锁;await 返回前一定已重新获得锁
二、用多个 Condition 实现多条件精准唤醒(典型场景:生产者-消费者变体)
例如:缓冲区有“空”和“满”两个独立条件,希望“有空位”时只唤醒生产者,“有数据”时只唤醒消费者,互不干扰。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
<font color="#888">ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition(); // 等待“未满”
Condition notEmpty = lock.newCondition(); // 等待“非空”
// 生产者
void produce(Object item) throws InterruptedException {
lock.lock();
try {
while (buffer.size() == capacity) {
notFull.await(); // 满了就等“未满”
}
buffer.add(item);
notEmpty.signal(); // 通知至少有一个元素了
} finally {
lock.unlock();
}
}
// 消费者
Object consume() throws InterruptedException {
lock.lock();
try {
while (buffer.isEmpty()) {
notEmpty.await(); // 空了就等“非空”
}
Object item = buffer.remove(0);
notFull.signal(); // 通知至少有一个空位了
return item;
} finally {
lock.unlock();
}
}</font>
关键点:两个 Condition 分离了语义,notFull.signal() 只唤醒因“满”而 await 的生产者,不会误唤醒正在等“非空”的消费者。
三、更复杂的多角色协作(如读写锁+优先级等待)
假设系统支持高优写、普通写、读三类操作,且要求:高优写到达时,应中断普通写和读;普通写不能插队高优写;读可并发但需避让所有写。
- 定义三个 Condition:
highWriteWait、normalWriteWait、readWait - 高优写 acquire 时,先 signalAll(
highWriteWait),再 await 自身(如果需要排队),同时对normalWriteWait和readWait执行 signalAll 让它们检查是否需让路 - 每个线程 await 前都检查“是否被更高优先级抢占”,用 while 循环配合状态变量(如 volatile int priorityLevel)做守卫
- 真正唤醒后,仍需在 lock 内重新校验业务条件,防止信号丢失或状态变化
四、避坑提醒:常见错误与健壮写法
- 永远用 while 而非 if 包裹 await:防止虚假唤醒或唤醒后条件已变(如多个线程被 signalAll,只有第一个能成功操作,其余必须重检)
- await 必须在 try-finally 外、lock 之后:确保锁已持有时才进入等待;但 await 本身会自动释放锁,所以 unlock 不能放在 await 后面
- 不要跨 Condition 共享同一个等待逻辑:每个 Condition 应对应唯一语义的等待原因
- signal 前最好先修改共享状态(如 buffer.add),再 signal,避免唤醒后检查失败又 await —— 这叫“状态先行”原则
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










