condition.await()可能虚假唤醒是设计使然,需用while循环检查条件;signal()须在持锁时调用并及时unlock;多线程竞争时应选signalall();condition与object.wait()/notify()不可混用。

Condition.await() 为什么总被虚假唤醒?
不是 bug,是设计使然。await() 允许在没有 signal() 的情况下返回,这是 JVM 规范明确要求的,用来应对信号中断、系统调度等不可控因素。你不能依赖“没 signal 就不会醒”,必须配合循环检查条件变量。
- 永远用
while (condition != expected)包裹await(),别用if - 条件变量必须是
volatile或由同一把锁保护(Condition所属的ReentrantLock) - 虚假唤醒在 Linux 上较常见,尤其高负载或容器环境,别在测试环境没复现就认为安全
signal() 唤不醒 await()?先查锁状态
signal() 不是发消息,它只是标记“有个线程可以醒了”,真正唤醒发生在持有锁的线程调用 unlock() 后。如果调用 signal() 时锁没释放,等待线程还在阻塞队列里,根本收不到信号。
- 确保
signal()和await()使用的是同一个Condition实例(别 new 多个) -
signal()必须在lock()持有状态下调用,但关键的是:之后要尽快unlock() - 常见错误:在
try块里lock(),却在finally外调用signal(),导致锁未释放就退出
signal() vs signalAll():选哪个看协作模型
单个生产者 + 单个消费者,用 signal() 足够;但凡涉及多个等待线程竞争同一资源(比如线程池任务队列满时的多个提交者),必须用 signalAll(),否则可能死锁。
-
signal()只唤醒一个线程,但不保证唤醒谁——JVM 自由选择,无法预测 - 用
signal()时,若被唤醒线程发现条件仍不满足(比如缓冲区还是空),它会继续await(),但其他线程可能永远等不到机会 -
signalAll()开销略大,但在多数业务场景下差异可忽略;宁可多唤醒,别漏唤醒
Condition 和 Object.wait()/notify() 能混用吗?
绝对不能。它们底层机制完全不同:Object 监视器锁和 ReentrantLock 的等待队列完全隔离。混用等于往两个不同邮箱发信,对方永远收不到。
- 用了
ReentrantLock就必须配Condition,别在 lock 块里写wait() - 反向也不行:synchronized 块里不能调用
condition.await(),会抛IllegalMonitorStateException - 迁移旧代码时,最容易漏改的就是日志打印后的临时调试语句,比如
System.out.println(...); wait();—— 这种地方要重点扫
最麻烦的不是语法错,而是逻辑上“以为唤醒了”:线程醒了,条件却没变,又立刻回去睡;或者多个线程抢着处理同一个事件,结果数据错乱。这些都得靠条件变量的严格守卫和每次唤醒后的二次校验来兜底。










