防止虚假唤醒需始终用while循环检查条件谓词,而非if;配合notifyAll()确保所有线程重检;条件判断、wait()及后续操作须在同个同步块内完成;不同业务条件应使用独立Condition。

防止虚假唤醒(Spurious Wakeup)导致的逻辑错误,核心是**永远在循环中检查条件变量所保护的谓词(predicate),而不是用 if 语句**。Java 和 POSIX 线程规范都明确允许虚假唤醒,它不是 bug,而是底层实现(如信号、中断、多核调度优化)带来的合法行为。
必须用 while 循环等待条件,而非 if
虚假唤醒会让线程在条件未真正满足时被唤醒。如果只用 if (condition) 判断,线程会直接执行后续逻辑,造成数据不一致或业务异常。
- ❌ 错误写法(可能出错):
if (!queue.isEmpty()) {
item = queue.poll();
}
- ✅ 正确写法(强制重检):
while (queue.isEmpty()) {
wait(); // 或 condition.await()
}
item = queue.poll();
配合 notifyAll()(或 signalAll())更安全
使用 notify() 只唤醒一个等待线程,若该线程因虚假唤醒或条件不满足而再次 wait,其他本应被唤醒的线程可能永久阻塞(尤其在多个条件共用同一锁/条件变量时)。
- 当有多个不同等待条件(如“非空”和“未满”)共享同一个锁时,优先用
notifyAll(),确保所有相关线程都有机会重新评估自身条件 -
notify()仅适用于:确定只有一个线程能推进状态,且唤醒目标唯一(如单生产者-单消费者+严格交替) - 性能差异在现代 JVM 中通常可忽略,安全性远大于微小开销
确保 wait() 前后条件判断原子性
谓词检查、wait() 调用、以及唤醒后的操作,必须全部在同一个同步块(synchronized)或显式锁(Lock + Condition)保护下完成。
- 不能把条件检查放在同步块外,否则存在竞态:检查通过 → 被抢占 → 条件被改 → 进入 wait → 永久挂起
- Java 示例(正确结构):
synchronized (lock) {
while (!ready) {
lock.wait();
}
// 使用资源...
}
- ReentrantLock + Condition 同理,必须在 lock() 和 unlock() 之间完成整个 while-check-wait 流程
避免在条件变量上混用多种等待逻辑
一个 Object(或 Condition)不应同时承载多个互斥的业务条件(如“缓冲区非空”和“缓冲区未满”)。否则即使用了 while,也可能因 notifyAll 唤醒不匹配的线程,徒增无效重检。
- 推荐为每类独立条件创建专属 Condition(如 notEmpty、notFull)
- 这样可用
notEmpty.signal()精准唤醒消费者,notFull.signal()唤醒生产者,减少干扰 - 也便于调试和理解线程间协作意图

















