Java中Condition实现精确唤醒的核心是:用同一Lock创建多个Condition实例,使不同线程在各自Condition上等待,并通过指定Condition.signal()定向唤醒对应队列中的线程,避免了Object.wait/notify的“惊群效应”和误唤醒。

Java 中使用 Condition 实现精确唤醒,核心在于:**用同一个 Lock 对象创建多个 Condition 实例,不同线程在不同 Condition 上等待,再通过指定的 Condition.signal() 唤醒对应等待队列中的线程**。这比 Object.wait()/notify() 更灵活,避免了“惊群效应”和误唤醒。
一、为什么需要 Condition 而不是 synchronized + wait/notify
synchronized 块中只能有一个隐式等待队列,notify() 随机唤醒一个,notifyAll() 全唤醒——无法区分“该唤醒谁”。比如生产者-消费者模型中,有空位时只该唤醒等待生产的线程;有数据时只该唤醒等待消费的线程。Condition 支持为不同等待逻辑定义独立的条件队列,实现定向通知。
二、基本使用步骤(以生产者-消费者为例)
假设一个固定容量的缓冲区,需控制“生产等待”和“消费等待”两个独立条件:
- 用
ReentrantLock替代synchronized - 调用
lock.newCondition()创建两个Condition:notFull(缓冲区未满)和notEmpty(缓冲区非空) - 生产者在
notFull.await()等待,成功生产后调用notEmpty.signal()唤醒消费者 - 消费者在
notEmpty.await()等待,成功消费后调用notFull.signal()唤醒生产者
三、关键细节与注意事项
必须在 lock 保护下调用 await/signal:否则抛 IllegalMonitorStateException。await 会自动释放锁并挂起线程;被 signal 唤醒后需重新竞争锁,获得锁后才从 await 返回。
立即学习“Java免费学习笔记(深入)”;
推荐用 while 循环检查条件:防止虚假唤醒(spurious wakeup)或条件在唤醒后又被其他线程改变。例如:
while (buffer.isEmpty()) {
notEmpty.await();
}
signal() vs signalAll():单个线程满足条件时用 signal() 更高效;若多个线程都可能符合条件(如多个消费者等待同一数据),可用 signalAll();但注意它不保证唤醒顺序。
四、完整可运行示例片段
以下是一个简化的线程安全阻塞队列核心逻辑:
class BoundedBuffer {
private final Lock 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;
BoundedBuffer(int capacity) { this.capacity = capacity; }
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();
}
}
}


















