java中用wait()/notify()实现生产者消费者,必须在synchronized块内对同一锁对象调用,用while循环防虚假唤醒,优先选notifyall()确保正确性,且wait释放锁而notify不释放。

Java 多线程中用 wait() 和 notify() 实现生产者消费者,核心是让线程在不满足条件时主动让出锁并等待,条件满足后再被唤醒继续执行。关键不在“调用方法”,而在于“怎么配合 synchronized 正确使用”。
必须用 synchronized 包裹 wait/notify
这三个方法(wait、notify、notifyAll)只能在持有对象监视器锁的前提下调用,否则会抛 IllegalMonitorStateException。所以它们一定出现在 synchronized 块或同步方法内,且锁对象要一致。
- 生产者和消费者必须对同一个共享对象加锁(比如缓冲区实例或一个专用锁对象)
- 不能在一个对象上
synchronized,却在另一个对象上调用wait() - 推荐显式传入锁对象,避免隐式使用
this引发歧义
用 while 循环判断条件,别用 if
虚假唤醒(spurious wakeup)可能发生——线程没被显式唤醒,但也从 wait() 返回了。如果只用 if 判断一次,可能直接往下执行,导致逻辑错误(比如从空队列取元素)。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 缓冲区满时,生产者必须
while (queue.size() == capacity)再wait() - 缓冲区空时,消费者必须
while (queue.isEmpty())再wait() - 每次从
wait()返回后,都要重新检查条件是否真正满足
notifyAll 比 notify 更稳妥
虽然 notify() 只唤醒一个等待线程,看似更轻量,但在多生产者或多消费者场景下容易出问题:
- 如果唤醒的是同类型线程(比如唤醒了另一个生产者,但缓冲区依然满),它又会立刻
wait(),造成无谓等待 -
notifyAll()唤醒所有等待者,由它们各自重新判断条件,自然选出能继续执行的那个 - 除非明确知道只有一个线程在等某个特定条件,否则默认用
notifyAll()
wait 会释放锁,notify 不会立即释放
这是理解协作流程的关键细节:
-
wait()被调用时,线程立刻释放当前持有的锁,进入等待队列 - 其他线程才能获取该锁,修改状态(如消费数据),然后调用
notifyAll() -
notifyAll()只是把等待线程标记为“可竞争”,并不释放锁;当前线程会继续执行完synchronized块,退出时才释放锁 - 被唤醒的线程需重新竞争锁,拿到后才从
wait()返回,再次检查条件
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










