Java多线程生产者消费者模型:synchronized与wait/notify实现
生产者消费者是Java多线程编程中的核心面试题,旨在解决多个线程对共享资源的竞争问题。本文将通过一个完整的Java代码示例,演示如何利用 synchronized 关键字确保同一时刻只有一个线程访问容器,并结合 wait() 和 notifyAll() 方法实现线程间的协调通信。
一、核心逻辑与问题分析
生产者消费者问题的核心在于“同步”与“互斥”。我们需要定义一个固定容量的容器,并遵循以下规则:
- 互斥访问:无论是生产还是消费,同一时间只能有一个线程操作容器,防止数据错乱。
- 满则停(生产者):当容器装满时,生产者必须暂停等待,直到有空间释放。
- 空则停(消费者):当容器为空时,消费者必须暂停等待,直到有新商品产生。
- 唤醒机制:生产者放入商品后需通知消费者;消费者取走商品后需通知生产者。
二、环境准备与代码结构
为了实现上述逻辑,我们定义了以下几个关键类:
Goods:代表具体的商品信息,如ID。Container:作为共享的仓库资源,负责处理加锁、入栈(push)、出栈(pop)以及线程状态管理。Producer:生产者线程,循环执行“生产-放入-休眠”。Consumer:消费者线程,循环执行“取出-消费-休眠”。
三、真实操作步骤:完整代码实现
第1步:构建共享容器与同步逻辑
目的:创建线程安全的队列,利用内置锁和等待队列控制流程。
动作:在 Container 类中,使用数组存储商品。方法上使用 synchronized 修饰以确保原子性。
class Container {
private int index = 0;
Goods[] goods = new Goods[6];
// 生产商品(入栈)
public synchronized void push(Goods good) {
// 如果满了,生产者是挂起
while (index == goods.length) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 存放商品并移动索引
goods[index] = good;
index++;
// 通知所有等待的线程(唤醒消费者)
notifyAll();
}
// 消费商品(出栈)
public synchronized Goods pop() {
// 如果空了,消费者挂起
while (index == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 移动索引,获取商品
index--;
Goods good = goods[index];
// 通知所有等待的线程(唤醒生产者)
notifyAll();
return good;
}
}
第2步:实现生产者与消费者线程
目的:分别定义生产和消费的行为及循环条件。
动作:Producer 和 Consumer 均实现 Runnable 接口,持有容器的引用,模拟生产或消耗过程。
class Producer implements Runnable {
Container con;
public Producer(Container con) {
this.con = con;
}
public void run() {
for (int i = 0; i < 20; i++) {
Goods good = new Goods(i);
con.push(good);
System.out.println("生产了:" + good);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
Container con;
public Consumer(Container con) {
this.con = con;
}
public void run() {
for (int i = 0; i < 20; i++) {
Goods good = con.pop();
System.out.println("消费了:" + good);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
第3步:启动主程序测试
入口:main 方法。
参数:创建一个 Container 实例,并分别传入给生产者、消费者线程对象。
结果:观察控制台打印顺序,验证是否满足“先进先出”且不会出现死锁或并发错误。
public class ProducerConsumerTest {
public static void main(String[] args) {
Container con = new Container();
Producer p = new Producer(con);
Consumer c = new Consumer(con);
new Thread(p).start();
new Thread(c).start();
}
}
四、完成判断与排错指南
成功标准:
1. 程序无报错终止。
2. 生产者未生产超过6个商品前,消费者能正常消费。
3. 生产者填满容器后不再打印,直到消费者取出商品后才继续。
常见问题排查:
- 漏掉 while 判断:如果在判断条件(如 `if (index==goods.length)`)后直接调用 `wait()`,可能会因为虚假唤醒导致后续逻辑出错。必须使用 `while` 循环来检查条件是否真的满足。
- 丢失 this.notifyAll():如果在 wait() 之前忘记调用唤醒方法,线程将永远处于挂起状态,导致程序卡死。
五、简要总结
这个示例展示了Java中最基础的线程同步原语。虽然现代开发中常用 `java.util.concurrent` 包下的阻塞队列(如 `BlockingQueue`)来简化此过程,但理解 synchronized、wait 和 notify 的组合机制,对于深入理解JVM内存模型和多线程设计至关重要。
以上就是Java多线程生产者消费者模式的详细内容,更多关于Java并发编程的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









