wait()和notify()必须在synchronized块中调用,且锁对象与等待对象一致;条件检查必须用while而非if以防虚假唤醒;多条件场景应优先使用notifyAll()。

wait() 和 notify() 必须在 synchronized 块中调用
直接调用 wait() 或 notify() 会抛出 IllegalMonitorStateException,因为这两个方法操作的是对象的“内置锁(monitor)”,而只有持有该对象锁的线程才能调用它们。
- 必须用
synchronized(obj) { ... }包裹,且obj是调用wait()/notify()的目标对象 - 不能用
this.wait()却在synchronized(otherObj)里——锁对象和等待对象必须一致 - 推荐显式传入一个专用的锁对象(如
private final Object lock = new Object();),避免与业务同步逻辑冲突
为什么一定要用 while 而不是 if 检查条件
wait() 返回后,被唤醒的线程不保证条件已满足——可能因虚假唤醒(spurious wakeup)或多个线程竞争导致状态再次失效。用 if 只检查一次,极易跳过条件判断直接执行后续逻辑,引发错误。
- 正确写法是
while (!condition) { obj.wait(); } - 每次从
wait()返回都重新校验条件,确保安全 - 例如生产者-消费者中,消费者不能只写
if (queue.isEmpty()) wait();,而应写while (queue.isEmpty()) wait();
notify() 与 notifyAll() 的选择直接影响线程行为
notify() 随机唤醒一个等待线程,notifyAll() 唤醒所有等待线程。二者语义不同,误用会导致死锁或吞吐下降。
- 当多个线程等待**同一条件但互斥**(如只有一个缓冲区槽位空闲),可用
notify() - 当等待线程关注**不同条件**(如既有“非空”又有“非满”等待者),必须用
notifyAll(),否则可能唤醒错的线程,导致无人响应真正就绪的条件 - 多数场景(尤其涉及多个条件变量时)优先选
notifyAll();性能敏感且逻辑绝对确定时才考虑notify()
public class BoundedBuffer {
private final List buffer = new ArrayList<>();
private final int capacity = 10;
private final Object lock = new Object();
public void put(int item) throws InterruptedException {
synchronized (lock) {
while (buffer.size() == capacity) {
lock.wait(); // 等待“有空位”
}
buffer.add(item);
lock.notifyAll(); // 唤醒所有等待者:可能有消费者在等“非空”
}
}
public int take() throws InterruptedException {
synchronized (lock) {
while (buffer.isEmpty()) {
lock.wait(); // 等待“非空”
}
int item = buffer.remove(0);
lock.notifyAll(); // 唤醒所有等待者:可能有生产者在等“未满”
return item;
}
}
}
最常被忽略的一点:即使逻辑看似简单,只要涉及多个条件、多个线程角色或未来可能扩展,就别省略 while 循环和 notifyAll()。JVM 不保证唤醒顺序,也不阻止虚假唤醒——靠机制兜底,而不是靠“我这次应该不会出问题”的假设。
立即学习“Java免费学习笔记(深入)”;










