Condition是Object.wait/notify的高级替代,依托ReentrantLock支持多条件队列、精准唤醒和响应中断;而wait/notify仅绑定synchronized且共用单一等待队列,易导致随机唤醒与虚假唤醒。

Java 中使用 Condition 实现精确的线程等待与唤醒,核心在于配合 ReentrantLock,通过多个 Condition 实例为不同等待条件建立独立的等待队列,避免 wait/notify 的“随机唤醒”和“虚假唤醒”问题。
为什么不用 Object 的 wait/notify?
Object.wait() 和 notify() 只能搭配 synchronized,且一个对象只有一个内置等待队列。调用 notify() 无法指定唤醒哪个条件下的线程,容易造成:
– 唤醒不相关的线程(浪费唤醒 + 再次等待)
– 必须用 while 循环检测条件(防虚假唤醒)
– 难以实现“某类线程等 A 条件,另一类等 B 条件”的分离控制
Condition 的基本协作流程
每个 Condition 必须绑定到一个 ReentrantLock 实例,典型步骤如下:
- 获取锁:
lock.lock() - 检查业务条件,不满足则调用
condition.await()(自动释放锁,进入该 condition 的等待队列) - 被唤醒后,
await()返回前会重新获取锁,需再次检查条件(防虚假唤醒) - 满足条件后执行业务逻辑,最后调用
condition.signal()或signalAll()唤醒等待在该 condition 上的线程 - 务必在
finally块中lock.unlock()
多条件等待:生产者-消费者示例
用两个 Condition 分别管理“缓冲区空”和“缓冲区满”两种等待场景:
立即学习“Java免费学习笔记(深入)”;
class BoundedBuffer {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition(); // 消费者等“非空”
private final Condition notFull = lock.newCondition(); // 生产者等“未满”
private final List<String> buffer = new ArrayList<>();
private final int capacity = 10;
void put(String item) throws InterruptedException {
lock.lock();
try {
while (buffer.size() == capacity) {
notFull.await(); // 等待有空位
}
buffer.add(item);
notEmpty.signal(); // 通知至少有一个元素可取
} finally {
lock.unlock();
}
}
String take() throws InterruptedException {
lock.lock();
try {
while (buffer.isEmpty()) {
notEmpty.await(); // 等待有数据
}
String item = buffer.remove(0);
notFull.signal(); // 通知至少有一个空位可用
return item;
} finally {
lock.unlock();
}
}
}
关键点:
– notFull.await() 只会让生产者挂起,不影响消费者等待 notEmpty
– signal() 是精准唤醒(例如只唤醒一个等待 notEmpty 的消费者),不是广播
– while 循环检查条件是必须的,不能用 if
注意细节与常见陷阱
- Condition 必须由创建它的 Lock 创建:不能跨 lock 复用,也不能用 synchronized 配合 Condition
- await() 会完全释放锁,并在唤醒后重新竞争获取锁:这和 Object.wait() 行为一致,但更可控
- signal() 不保证立即唤醒:它只是将线程从等待队列移到同步队列,真正执行仍需抢到锁
-
避免 signal() 在无等待线程时调用:虽然安全,但信号丢失;如需确保不丢,可结合状态变量或使用
signalAll()(慎用,可能唤醒过多) - 不要在 await() 后直接写业务逻辑:必须再次检查条件,因为可能被虚假唤醒或条件已变
Condition 提供了比传统 wait/notify 更清晰、更安全、更灵活的线程协作机制,特别适合需要区分多种等待原因的并发场景。用好它的关键是理解“条件变量 + 显式锁 + 循环检查”三位一体的设计思想。


















