java中wait()和notify()必须在synchronized块中对同一对象调用,且需用while循环检查条件以防止虚假唤醒;notify()唤醒单个线程,notifyall()唤醒所有,选择取决于等待逻辑是否同质。

Java中用wait()和notify()实现线程协作,核心不是“调用就生效”,而是必须配合synchronized锁、在正确对象上操作,并严格遵循“检查-等待-使用”循环模式。漏掉任一环节都可能造成死锁、虚假唤醒或IllegalMonitorStateException。
必须在synchronized块中调用
wait()、notify()、notifyAll()只能在持有对应对象监视器(monitor)时调用,否则抛出IllegalMonitorStateException。也就是说,它们必须出现在synchronized(obj) { ... }内部,且obj必须与调用方法的对象一致。
- 错误写法:
obj.wait()但没对obj加锁,或锁的是this却调用obj.wait() - 正确写法:
synchronized(obj) { obj.wait(); }或synchronized(this) { this.wait(); } - 注意:
wait()会自动释放当前持有的锁,线程进入WAITING状态;被唤醒后需重新竞争该锁,获得后才继续执行
永远用while循环检查条件,不用if
线程被唤醒不等于条件已满足——可能因虚假唤醒(spurious wakeup)或其它线程抢先修改了状态。因此,wait()必须放在while循环中,每次唤醒后重新校验业务条件。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 反例(危险):
if (queue.isEmpty()) wait();→ 唤醒后直接消费,可能NPE - 正例(安全):
while (queue.isEmpty()) wait();→ 唤醒后先再判断,不满足则继续等 - 典型场景:生产者-消费者中,消费者要等队列非空,生产者要等队列未满,都必须用
while
notify() vs notifyAll():按协作模型选
notify()随机唤醒一个等待线程,notifyAll()唤醒所有等待线程。选择取决于等待者是否“同质”以及条件是否互斥。
- 用
notify():当多个线程等待同一条件,且只需一个被唤醒就能推进(如单个任务队列,一个消费者取走任务后,其他消费者仍无事可做) - 用
notifyAll():当等待条件不同,或唤醒一个可能导致状态不一致(如同时有“队列空”和“队列满”两个条件在同一个锁对象上等待) - 常见陷阱:只用
notify()但存在多种等待逻辑,结果唤醒错的线程,它检查条件不满足又回去等,真正需要的线程却一直没被唤醒
推荐搭配volatile + while + synchronized的标准模板
一个健壮的等待/通知结构通常包含三要素:可变状态(建议volatile修饰)、同步块保护、循环检查。例如实现简单阻塞队列的take操作:
private final Object lock = new Object();
private volatile boolean ready = false;
public void waitForReady() throws InterruptedException {
synchronized (lock) {
while (!ready) {
lock.wait();
}
// 此处ready为true,且已重新获得lock
}
}
public void setReady() {
synchronized (lock) {
ready = true;
lock.notify(); // 或 notifyAll()
}
}
volatile保证状态变更对所有线程可见,synchronized保证原子性和内存语义,while防御虚假唤醒——三者缺一不可。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










