java手写生产者消费者模型需用synchronized块配合wait()/notifyall(),核心是共享缓冲区、while循环判断条件、统一锁对象;示例中boundedbuffer通过put/take实现线程协作,必须用while防虚假唤醒,notifyall避免死锁。

Java 中通过 wait/notify 手写生产者消费者模型,核心是用一个共享缓冲区(比如 ArrayList 或数组)配合 synchronized 块 + wait() / notifyAll() 实现线程协作。关键点在于:必须在同步块中调用、用 while 而非 if 判断条件、每次唤醒后重新检查状态。
共享资源与同步控制
定义一个带容量限制的容器(如固定大小的数组),并封装 put() 和 take() 方法。所有对容器的操作必须在 synchronized 块中进行,锁对象建议用容器自身(如 this 或专用锁对象),避免锁粒度太粗或太散。
示例结构:
class BoundedBuffer {
private final Object[] buffer;
private int head = 0, tail = 0, count = 0;
private final int capacity;
public BoundedBuffer(int capacity) {
this.capacity = capacity;
this.buffer = new Object[capacity];
}
public synchronized void put(Object item) throws InterruptedException {
while (count == capacity) {
wait(); // 满了就等待
}
buffer[tail] = item;
tail = (tail + 1) % capacity;
count++;
notifyAll(); // 唤醒可能等待消费的线程
}
public synchronized Object take() throws InterruptedException {
while (count == 0) {
wait(); // 空了就等待
}
Object item = buffer[head];
buffer[head] = null;
head = (head + 1) % capacity;
count--;
notifyAll(); // 唤醒可能等待生产的线程
return item;
}
}
生产者与消费者线程实现
分别创建生产者和消费者线程类,复用上面的 BoundedBuffer 实例。注意捕获 InterruptedException 并合理处理(通常恢复中断状态或退出)。
- 生产者循环调用
buffer.put(item),可加随机休眠模拟真实耗时 - 消费者循环调用
buffer.take(),打印或处理拿到的数据 - 多个生产者/消费者可共用同一个缓冲区实例,天然支持并发
为什么必须用 while 而不是 if?
wait() 可能被虚假唤醒(spurious wakeup),也可能多个线程被 notifyAll() 同时唤醒,但只有部分满足条件。如果用 if,线程唤醒后直接往下执行,可能造成越界或空取。用 while 能确保每次唤醒后都重新校验条件是否真正成立。
例如:while (count == 0) wait(); 比 if (count == 0) wait(); 安全得多。
注意事项与常见坑
-
锁对象要一致:
wait()和notifyAll()必须在同一个锁对象上调用,否则无效 -
不能在静态方法中直接调用非静态的
wait/notify:因为它们依赖实例锁 -
不要用
notify()替代notifyAll():除非你完全确定只有一个等待线程,否则容易导致死锁 -
异常处理别忽略中断:捕获
InterruptedException后应调用Thread.currentThread().interrupt()恢复中断状态
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











