condition.await()不能替代synchronized.wait(),因它必须绑定reentrantlock,需先lock.lock()再await(),且await()自动释放并重获锁;支持多条件精准唤醒、中断响应和超时等待,但须用while循环防虚假唤醒。

Condition.await() 为什么不能替代 synchronized.wait() 直接用
因为 Condition 必须绑定在 ReentrantLock 上,不是任意对象都能调用——它不依赖对象监视器(monitor),而是靠 lock + condition 的显式配对。直接拿 synchronized 块里的对象去 new Condition?编译都过不去。
常见错误现象:IllegalMonitorStateException 不会出现,但你会卡在 lock.newCondition() 之前就发现没 lock 根本没法建 condition;或者忘了 lock.lock() 就调 condition.await(),结果线程永远阻塞(因为 await 前必须已持有 lock)。
- 必须先
lock.lock(),再condition.await(),且 await 会自动释放该 lock -
await()返回前会重新竞争并获取 lock,所以后续代码仍处于临界区 - 别在
synchronized方法里混用Condition,语义冲突,JVM 不拦你,但逻辑必乱
多个 Condition 怎么对应不同等待场景(比如 notEmpty / notFull)
一个 ReentrantLock 可以创建多个 Condition 实例,这是精准唤醒的核心:生产者只唤醒等消费的线程,消费者只唤醒等生产的线程,避免 notifyAll() 的“全量惊群”。
使用场景:有界队列(如 ArrayBlockingQueue 内部就是这么干的)。你需要两个独立等待队列——一个存“等数据”的消费者,一个存“等空间”的生产者。
立即学习“Java免费学习笔记(深入)”;
- 定义两个 condition:
notEmpty = lock.newCondition()、notFull = lock.newCondition() - 消费者取数据前
notEmpty.await(),取完后notFull.signal()(告诉生产者“有空位了”) - 生产者放数据前
notFull.await(),放完后notEmpty.signal()(告诉消费者“有数据了”) - 别用
signal()代替signalAll()除非你确认只有一个线程在等——否则可能漏唤醒
signal() 和 signalAll() 选哪个?什么时候会丢唤醒
signal() 只唤醒一个在该 condition 上等待的线程,signalAll() 唤醒全部。看似 signalAll() 更安全,但它会引发不必要的竞争,尤其在高并发写入时拖慢吞吐。
容易踩的坑:用 signal() 却没检查循环条件,导致虚假唤醒后直接往下跑,读到空/满状态还硬操作。
- 所有
await()必须写在 while 循环里,不能用 if ——例如while (queue.isEmpty()) notEmpty.await(); -
signal()安全的前提是:每次状态变更只影响一个等待方,且你确保最多一个线程在等这个条件(比如单消费者模型) - 如果生产者和消费者都是多线程,又用了
signal(),但多个消费者同时被唤醒后抢同一个元素,就可能出现IndexOutOfBoundsException或空指针
中断响应与超时 await 的实际处理方式
Condition 支持响应中断(await() 抛 InterruptedException)和带超时的等待(awaitNanos(long)),比 synchronized + wait() 更灵活,但也更易出错。
性能影响:超时等待本身开销不大,但频繁进出阻塞态会增加线程调度负担;中断则需上层主动管理中断状态,否则可能静默吞掉异常。
- 捕获
InterruptedException后,通常应恢复中断状态:Thread.currentThread().interrupt(); - 不要在 catch 里只打日志然后继续跑——这会让线程失去“被中断”的语义,上游无法感知
-
awaitNanos()返回值是剩余纳秒数,≤ 0 表示超时,> 0 表示被 signal 唤醒,要结合 while 循环重检条件 - 避免在 finally 里无条件
lock.unlock()——如果 await 被中断,lock 已释放,再 unlock 会抛IllegalMonitorStateException
await() 前没加 while 循环,以及 signal() 后没考虑是否真有线程在等那个 condition。










