wait()和notify()必须在synchronized块中调用,否则抛illegalmonitorstateexception;wait()释放锁并进入等待队列,notify()随机唤醒一个线程使其竞争锁,需用while循环重检条件以防虚假唤醒。

Java 对象监视器中的 wait() 和 notify() 是基于内置锁(即 synchronized 锁)实现线程间协作的核心机制,它们本身不直接“通信”,而是通过共享对象的监视器状态变化来协调线程执行顺序。
wait() 和 notify() 必须在 synchronized 块中调用
这是强制要求,否则会抛出 IllegalMonitorStateException。因为只有持有对象锁的线程,才有权操作该对象的等待队列。
-
wait()会让当前线程释放锁、进入对象的等待队列,并暂停执行,直到被唤醒或中断 -
notify()从该对象的等待队列中随机唤醒一个线程(不保证公平性),被唤醒的线程需重新竞争锁,获得锁后才能继续执行 -
notifyAll()唤醒所有等待线程,适用于多个线程等待不同条件的场景(如生产者-消费者中多个消费者都在等数据)
典型协作模式:检查条件 + 循环等待
不能用 if 判断条件后直接 wait(),必须用 while 循环重检。因为存在虚假唤醒(spurious wakeup)和条件变更竞争问题。
例如实现一个简单阻塞队列的取元素逻辑:
synchronized (queue) {
while (queue.isEmpty()) {
queue.wait(); // 释放锁,进入等待
}
return queue.remove();
}
对应地,插入元素后应通知:
synchronized (queue) {
queue.add(item);
queue.notify(); // 或 notifyAll()
}
底层依赖 JVM 的对象监视器(Object Monitor)
每个 Java 对象都有一个关联的监视器,由 JVM 管理。synchronized 进入时尝试获取监视器锁;wait() 会把线程放入该监视器的等待集合(WaitSet),并标记为 WAITING 状态;notify() 将一个线程从 WaitSet 移到同步队列(EntryList),使其参与锁竞争。
注意:wait() 返回前一定会重新获取锁 —— 这是原子过程,无需手动加锁。
常见误用与注意事项
- 不要用字符串常量或全局共享对象(如
"lock")作为 wait/notify 的锁,容易引发意外唤醒或死锁 - 避免在循环中频繁调用
notify()而不检查实际条件是否满足,可能造成无效唤醒 -
wait(long timeout)可设超时,防止无限等待;但超时返回后仍需检查条件是否成立 - JUC 中的
Condition是更灵活的替代方案(支持多等待队列、精确唤醒),但底层原理仍类似
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











