必须用while循环而非if判断条件,因为JVM允许虚假唤醒——wait可能在未被notify时意外返回;while可反复验证条件,确保真正满足才执行后续逻辑。
使用 object.wait() 时必须配合 while 循环判断条件,而不是 if 判断,这是避免虚假唤醒(spurious wakeup)最核心、最有效的做法。
为什么虚假唤醒会发生?
虚假唤醒是指线程在没有被显式唤醒(notify() 或 notifyAll())的情况下,wait() 方法意外返回。JVM 规范允许这种行为——它不保证 wait 只在收到通知时才退出。操作系统层面的信号干扰、内部调度优化等都可能导致此现象。即使没有其他线程调用 notify,等待中的线程也可能“自己醒来”。
为什么必须用 while 而不是 if?
if 判断只检查一次条件,一旦 wait 返回(无论是被唤醒还是虚假唤醒),就直接往下执行,此时条件很可能仍不满足,导致逻辑错误(如消费空队列、重复处理、状态不一致等)。而 while 循环会在每次 wait 返回后重新验证条件,不满足就继续 wait,从而屏蔽虚假唤醒的影响。
-
正确写法(推荐):
synchronized (lock) {
while (!conditionMet()) {
lock.wait();
}
// 条件真正满足,安全执行后续逻辑
} -
错误写法(危险):
synchronized (lock) {
if (!conditionMet()) {
lock.wait(); // 一次判断,一次等待,醒后不复查
}
// 此处 condition 可能仍为 false!
}
配合 notify() / notifyAll() 的协作规范
仅靠 while 循环还不够,还需确保:当条件发生变化时,有线程负责唤醒等待者;且唤醒策略要匹配等待逻辑。
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
- 若多个线程等待不同条件(例如生产者/消费者共用同一锁但关注不同状态),优先使用
notifyAll(),避免因notify()唤醒错对象导致死锁或饥饿。 - 每次修改共享状态并可能使某个等待条件变为 true 后,必须在同一个同步块中调用
notify()或notifyAll()。 - 不要假设“只唤醒一个就够了”——除非你 100% 确保只有一个线程在等该条件,且唤醒顺序可控(极难保证)。
一个典型例子:阻塞队列的 take 操作
假设实现简易阻塞队列的取元素方法:
public synchronized E take() throws InterruptedException {while (queue.isEmpty()) {
wait(); // 条件不满足时等待
}
E item = queue.remove();
notifyAll(); // 可能有生产者在等空间,唤醒所有相关等待者
return item;
}
这里 while 检查 isEmpty() 是关键:即使被虚假唤醒,也会再次检查,不满足就继续 wait,绝不会尝试从空队列 remove。










