Java中Condition通过一个Lock配多个Condition实例实现按条件分类阻塞与定向唤醒,如notFull和notEmpty;await必须用while循环检查谓词防虚假唤醒;signal()唤醒单个线程更精准,signalAll()唤醒全部;状态更新、signal()、unlock()顺序不可颠倒。

Java 中 Condition 的 await() 和 signal() 本身不支持“多条件变量精准唤醒”,但可以通过为同一 Lock 创建多个 Condition 实例,配合业务逻辑判断和循环等待,实现**按条件分类阻塞与定向唤醒**——这才是所谓“精准唤醒”的实际做法。
一个 Lock 配多个 Condition 实现条件隔离
每个 Condition 是独立的等待队列,互不干扰。比如生产者-消费者场景中,可分别定义 notFull 和 notEmpty 两个条件:
Lock lock = new ReentrantLock();-
Condition notFull = lock.newCondition();(供生产者 await) -
Condition notEmpty = lock.newCondition();(供消费者 await)
这样,notFull.signal() 只唤醒在 notFull 上等待的生产者线程,不会误唤醒消费者,天然做到按语义精准区分。
await 必须配合 while 循环检查条件谓词
await() 不保证唤醒后条件一定成立(可能被虚假唤醒或其它线程抢先修改状态),所以必须用 while 而非 if:
立即学习“Java免费学习笔记(深入)”;
lock.lock();
try {
while (queue.size() == MAX_SIZE) {
notFull.await(); // 等待非满
}
queue.offer(item);
notEmpty.signal(); // 唤醒一个消费者
} finally {
lock.unlock();
}
同理,消费者也需 while (queue.isEmpty()) notEmpty.await();。这是避免逻辑错误的关键守则。
signal() 与 signalAll() 的选择影响唤醒精度
signal() 唤醒单个等待线程(JVM 选一个),适合“满足条件只需一个线程继续”的场景,如缓冲区刚腾出一个空位,只需唤醒一个生产者;signalAll() 唤醒全部,适合条件变化影响所有等待者,如关闭信号、重置状态等。
- 用
signal()更精准,减少无谓竞争 - 但要注意:若被唤醒线程因条件未真正满足而再次 await,可能造成“唤醒丢失”,此时需确保唤醒逻辑与条件更新严格配对
避免跨 Condition 误唤醒或死锁的设计要点
多个 Condition 共享同一把锁,需注意:
- 所有对共享状态的读写操作,必须在
lock保护下进行 - 唤醒操作(
signal())应在状态更新之后、锁释放之前执行,否则可能唤醒时看到旧状态 - 不要在持有锁时调用外部可能获取锁的方法,防止嵌套锁死
例如:先 queue.offer(),再 notEmpty.signal(),最后 unlock() —— 顺序不能颠倒。


















