wait()必须放在while循环里,因为可能伪唤醒,导致条件不成立时继续执行;if只检查一次,而while确保每次唤醒都重新验证条件,且需在synchronized块内操作以保证可见性和原子性。

为什么 wait() 必须放在 while 循环里,不能用 if
因为 wait() 可能被伪唤醒——线程没收到 notify() 或 notifyAll() 就自己醒了。这时候条件未必成立,直接往下走会出错。
常见错误现象:生产者还没放数据,消费者就从 wait() 返回并尝试取空队列,抛 EmptyStackException 或返回错误值。
- 伪唤醒是 JVM 规范允许的行为,和操作系统信号、线程调度机制有关,不是 bug
-
if判断只执行一次,醒过来就跳过检查;while保证每次醒来都重新验条件 - 即使你用的是
notifyAll(),也得用while——多个线程被唤醒后,只有一个是真符合条件的
wait() 前后必须用 synchronized 块包裹
否则会抛 IllegalMonitorStateException,而且条件变量的可见性无法保障。
使用场景:所有基于 Object.wait()/notify() 的协作逻辑,比如阻塞队列、信号量模拟、生产者-消费者。
立即学习“Java免费学习笔记(深入)”;
-
synchronized锁的对象,必须和wait()调用的目标对象一致(通常是this或共享锁对象) - 条件变量(如
queue.isEmpty())必须在同步块内读取,否则可能看到过期值 - 别在
ReentrantLock+Condition场景下误用Object.wait()——那是两套机制,混用会失效
标准写法长什么样?一个最小可运行例子
这是最常被抄错、但又最该记住的模板:
synchronized (lock) {
while (!conditionMet()) {
lock.wait();
}
// do something safe
}
其中 conditionMet() 是你要等的逻辑,比如 !queue.isEmpty() 或 count > 0。
-
wait()调用必须在while循环体内,不能提出来 - 别在循环里写
wait(1000)这种带超时的——超时后依然要检查条件,否则还是伪唤醒陷阱 - 如果用了
wait(long),记得判断返回时是否真满足条件,超时和伪唤醒无法区分
用 Lock + Condition 也逃不开 while
Condition.await() 同样会发生伪唤醒,JVM 不保证它比 Object.wait() 更“老实”。
性能/兼容性影响:虽然 Condition 支持多等待队列、可中断、更灵活,但循环检查逻辑一模一样。
- 写法是:
lock.lock(); try { while (!conditionMet()) { condition.await(); } } finally { lock.unlock(); } - 别以为换了 API 就不用防伪唤醒——规范白纸黑字写了 “spurious wakeups are possible”
- 如果你用的是
ArrayBlockingQueue这类 JDK 并发容器,它们内部已经帮你套好了while,但自己手写协作逻辑时绝不能省
while (!queue.isEmpty()) 中的 isEmpty() 如果不是同步方法,整个循环就失去意义。











