JeffyLu.github.io
JeffyLu.github.io copied to clipboard
Java多线程----多生产者多消费者的同步问题
Java多线程----多生产者多消费者的同步问题
生产者和消费者的同步问题是多线程编程中的一个经典同步问题。这边就以一个蛋糕店为例,来模拟多生产者和多消费者的同步问题。
功能说明
商店具备制作蛋糕和销售蛋糕两个功能,商店每天的蛋糕量是有限的,所以不能无限制的制作蛋糕。消费者在商店蛋糕有库存的情况下可以进行购买,库存不足并且蛋糕数量还未到达今天的峰值之前,消费者必须等待蛋糕制作完成才能进行购买。而只要在蛋糕数量还没到达峰值,生产者就会源源不断的生产。
构造一个商店
设置商店的几个基本属性
private ArrayList<String> cakes = new ArrayList<>();
private int maxProduce = 10; // 最大产量
private int count = 1; // 蛋糕编号
public boolean isSaleOut = false; // 是否售空
销售方法
为了防止同一份蛋糕被多次销售,这里为buy()方法增加同步机制synchronized。当得知蛋糕没有现货并且还在生产的时候,利用super.wait()让所有想买蛋糕的线程进入等待状态。
public synchronized void buy() {
// 蛋糕库存不足
if (cakes.isEmpty()) {
// 蛋糕还在生产中
if (!isSaleOut) {
try {
System.out.println(Thread.currentThread().getName()
+ ">>> 蛋糕制作中, 请稍等!");
// 顾客排队等候
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else { // 蛋糕卖完了
System.out.println(Thread.currentThread().getName()
+ ">>> 店员说今天的蛋糕卖完了!");
}
} else { // 有现货立马卖
System.out.println(Thread.currentThread().getName() + ">>> 购买: "
+ cakes.remove(0));
}
}
制作方法
同样的,为了不让一份蛋糕被多次制作,这里和销售方法一样要添加上同步机制。当一个蛋糕被生产完成的时候,可以用super.notifyAll()通知所有在等待的线程有蛋糕吃了。如果蛋糕数量已经到达了今天的峰值,厨师就可以休息了,这时候用Thread.yield()让出cpu给其他线程执行。
public synchronized void make() {
// 还没售空
if (!isSaleOut) {
// 赶紧制造
if (count <= maxProduce) {
cakes.add("蛋糕-" + count);
System.out.println(Thread.currentThread().getName()
+ ">>> 制作: 蛋糕-" + count);
count++;
// 通知排队的顾客
super.notifyAll();
} else {
isSaleOut = true;
System.out.println(Thread.currentThread().getName()
+ ">>> 材料不足, 无法继续生产!");
}
} else {
// 厨师没事干了让出线程
Thread.yield();
}
}
生产者类
生产者很单纯的只要不断生产就行。
class Producer implements Runnable {
private Shop shop = null;
Producer(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
// 不停的制作
while (true) {
shop.make();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者类
同样,消费者只管不停的买蛋糕。
class Consumer implements Runnable {
private Shop shop = null;
Consumer(Shop shop) {
this.shop = shop;
}
@Override
public void run() {
// 不停的购买
while (true) {
shop.buy();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
创建线程
这边新建了2个生产者和3个消费者。
// 创建商店、生产者、消费者
Shop myShop = new Shop();
Producer producer = new Producer(myShop);
Consumer consumer = new Consumer(myShop);
// 创建多个生产者和消费者
Thread pThread1 = new Thread(producer, "厨师-1 ");
Thread pThread2 = new Thread(producer, "厨师-2 ");
Thread cThread1 = new Thread(consumer, "顾客-1 ");
Thread cThread2 = new Thread(consumer, "顾客-2 ");
Thread cThread3 = new Thread(consumer, "顾客-3 ");
// 启动
pThread1.start();
pThread2.start();
cThread1.start();
cThread2.start();
cThread3.start();
最后来看看结果
厨师-1 >>> 制作: 蛋糕-1
厨师-2 >>> 制作: 蛋糕-2
顾客-3 >>> 购买: 蛋糕-1
顾客-2 >>> 购买: 蛋糕-2
顾客-1 >>> 蛋糕制作中, 请稍等!
厨师-1 >>> 制作: 蛋糕-3
顾客-3 >>> 购买: 蛋糕-3
顾客-2 >>> 蛋糕制作中, 请稍等!
厨师-2 >>> 制作: 蛋糕-4
顾客-1 >>> 购买: 蛋糕-4
顾客-3 >>> 蛋糕制作中, 请稍等!
厨师-1 >>> 制作: 蛋糕-5
顾客-2 >>> 购买: 蛋糕-5
厨师-2 >>> 制作: 蛋糕-6
顾客-1 >>> 购买: 蛋糕-6
厨师-1 >>> 制作: 蛋糕-7
顾客-2 >>> 购买: 蛋糕-7
厨师-2 >>> 制作: 蛋糕-8
顾客-3 >>> 购买: 蛋糕-8
顾客-1 >>> 蛋糕制作中, 请稍等!
厨师-1 >>> 制作: 蛋糕-9
顾客-2 >>> 购买: 蛋糕-9
厨师-2 >>> 制作: 蛋糕-10
顾客-3 >>> 购买: 蛋糕-10
厨师-1 >>> 材料不足, 无法继续生产!
顾客-1 >>> 店员说今天的蛋糕卖完了!
顾客-2 >>> 店员说今天的蛋糕卖完了!
顾客-3 >>> 店员说今天的蛋糕卖完了!
顾客-1 >>> 店员说今天的蛋糕卖完了!
源码:Producers&&Consumers.java
good