JeffyLu.github.io icon indicating copy to clipboard operation
JeffyLu.github.io copied to clipboard

Java多线程----多生产者多消费者的同步问题

Open JeffyLu opened this issue 9 years ago • 1 comments

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

JeffyLu avatar Nov 22 '16 05:11 JeffyLu

good

404100691 avatar Sep 06 '17 04:09 404100691