java中wait()、notify()、notifyall()必须在synchronized块内调用,否则抛illegalmonitorstateexception;wait()释放锁并进入waiting状态,被唤醒后需重新竞争锁;应使用while循环避免虚假唤醒;notify()随机唤醒一个线程,notifyall()唤醒所有等待线程。

Java 中可以通过 Object 的 wait()、notify() 和 notifyAll() 方法配合 synchronized 块,利用对象的监视器锁(monitor lock)实现线程间的等待与通知。核心前提是:这些方法必须在已获取该对象锁的同步上下文中调用,否则会抛出 IllegalMonitorStateException。
必须在 synchronized 块中调用 wait/notify
每个 Java 对象都关联一个内置的监视器锁。只有持有该锁的线程才能调用其 wait() 或 notify()。常见错误是忘记加锁或锁对象不一致。
- 调用
obj.wait()前,当前线程必须已通过synchronized(obj)获取了obj的锁 -
notify()和notifyAll()同理,也必须在同一个对象的同步块内执行 - 不要混用不同对象的锁——比如在
synchronized(a)里调用b.wait()是非法的
wait() 会释放锁并进入等待状态
wait() 不仅让线程挂起,还会主动释放当前持有的监视器锁,允许其他线程进入同一同步块;而 sleep() 或 join() 不释放锁。
- 线程调用
wait()后进入 WAITING 状态,等待被同锁对象上的notify()或notifyAll()唤醒 - 被唤醒后,线程不会立即执行,而是重新竞争该对象的锁;获得锁后才从
wait()返回 - 建议始终用
while循环检查条件,避免虚假唤醒(spurious wakeup)
使用 notify() 和 notifyAll() 的选择
notify() 随机唤醒一个等待线程,notifyAll() 唤醒所有等待线程。实际选用取决于业务逻辑是否允许多个线程同时推进。
- 生产者-消费者模型中,若只有一个共享缓冲区,通常用
notifyAll()更安全——避免因唤醒错类型线程(如唤醒另一个生产者而非消费者)导致死锁 - 如果能确保等待线程的条件互斥且数量可控(例如有明确角色分工),可用
notify()减少不必要的竞争 - 永远不要假设
notify()会唤醒“最该醒”的那个线程——JVM 不保证唤醒顺序
典型协作模式:以阻塞队列为例
以下是一个基于监视器锁的简易阻塞队列片段,体现等待通知的正确用法:
public class SimpleBlockingQueue<t> {
private final List<t> queue = new ArrayList();
private final int capacity;
public SimpleBlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T item) throws InterruptedException {
while (queue.size() == capacity) {
wait(); // 队满时等待
}
queue.add(item);
notifyAll(); // 通知可能等待 take 的消费者
}
public synchronized T take() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 队空时等待
}
T item = queue.remove(0);
notifyAll(); // 通知可能等待 put 的生产者
return item;
}
}</t></t>
注意:这里用 synchronized 修饰方法,等价于 synchronized(this),所以 wait() 和 notifyAll() 操作的是同一把锁;循环判断条件、统一用 notifyAll() 都是关键实践。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











