wait方法必须在synchronized块中调用,否则抛illegalmonitorstateexception;它会释放锁并使线程进入waiting状态,需配合notify/notifyall唤醒,且须用while循环验证条件、优先使用notifyall。

wait 方法必须在 synchronized 同步块中调用,否则会抛出 IllegalMonitorStateException;它会让当前线程释放锁并进入等待状态,直到被其他线程通过 notify 或 notifyAll 唤醒。
wait 的基本使用前提
Java 中的 wait() 是 Object 类的方法,不是 Thread 类的。这意味着任何对象都能作为“等待/通知”的协调点,但前提是当前线程必须持有该对象的监视器锁(即处于 synchronized 块或方法中)。
- 直接调用
obj.wait()而没加锁 → 抛出IllegalMonitorStateException -
wait()会立即释放 obj 的锁,让其他线程有机会获取并操作共享资源 - 线程进入 WAITING 状态,不再参与 CPU 竞争,直到被唤醒或中断
典型协作模式:生产者-消费者
这是 wait/notify 最常见的应用场景。多个线程共享一个缓冲区,生产者往里放数据,消费者从中取数据,双方需协调避免空取或满写。
- 缓冲区为空时,消费者调用
buffer.wait()放弃锁、等待生产者通知 - 缓冲区满时,生产者调用
buffer.wait()等待消费者消费后腾出空间 - 生产者放入数据后调用
buffer.notifyAll()(推荐),确保所有等待线程有机会重新竞争 - 消费者取出数据后同样调用
buffer.notifyAll()唤醒可能等待的生产者
为什么推荐用 notifyAll 而非 notify
notify() 只随机唤醒一个等待线程,而实际业务中常存在多个同类等待者(比如多个消费者都在等数据)。若只唤醒一个,其余线程可能永远无法被触发,导致死锁或饥饿。
- 例如:三个消费者都在 buffer 上 wait,生产者只 notify 一个 → 剩下两个一直阻塞
-
notifyAll()让所有等待线程回到锁竞争队列,由 JVM 调度谁先获得锁继续执行 - 虽然多唤醒会带来额外开销,但比遗漏唤醒更安全,逻辑也更可预测
唤醒后的正确处理方式
被唤醒的线程不会立刻执行,而是重新尝试获取锁;拿到锁后必须再次检查条件是否满足——因为可能存在虚假唤醒(spurious wakeup),或多个线程被唤醒后只有第一个能真正执行。
- 务必用
while循环判断条件,而不是if - 错误写法:
if (queue.isEmpty()) wait();→ 可能跳过条件检查直接执行,引发异常 - 正确写法:
while (queue.isEmpty()) wait();→ 唤醒后重新校验,不满足就继续 wait - 同理,notify 前也要确保条件已更新(如 add() 后 size > 0)
不复杂但容易忽略细节,核心就是三点:加锁才能 wait、用 while 判断条件、优先 notifyAll。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











