JUC学习笔记 -- (8) 多线程并发之生产者消费者模式的两种实现

生产者消费者模式是并发、多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据。

 

真实世界中的生产者消费者模式

生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者(那个吃的)等待如果桌子空了的话。这里桌子就是一个共享的对象。

我们看这样一个例子:

生产者:  往一个公共的盒子里面放产品

消费者:从公共的盒子里面取产品

盒子:盒子的容量不能超过5

下面我们用两者方法分别实现这样一个场景。

方法一:   wait()  和   notify()   通信方法实现

 

看盒子代码

       

class Box{
	private int product;

	public synchronized void addProduct(){
		while(product>=5){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		product++;
		System.out.println(Thread.currentThread().getName()+"生产了第:"+product+"个产品!");
		this.notifyAll();

	}

	public synchronized void consumeProduct(){
		while(product<=0){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"消费了第:"+product+"个产品!");
		product--;
		this.notifyAll();
	}
}

  

生产者代码(生产十次):

 

class Producer implements Runnable{

	private Box box;


	public Producer(Box box) {
		this.box = box;
	}
	@Override
	public void run() {
		for(int i=0;i<10;i++){
			try {
				Thread.currentThread().sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			box.addProduct();
		}
	}
}

 

消费者代码(同样十次):

class Consumer implements Runnable{

	private Box box ;


	public Consumer(Box box) {
		this.box = box;
	}
	@Override
	public void run() {
		for(int i=0;i<10;i++){
			try {
				Thread.currentThread().sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			box.consumeProduct();
		}
	}
}

测试代码:

public class TestProducerAndConsumer {

	public static void main(String[] args) {
		Box box = new Box();
		Producer Producer = new Producer(box);
		Consumer consumer = new Consumer(box);

		Thread t1 = new Thread(Producer,"生产者");
		Thread t2 = new Thread(consumer,"消费者");

		t1.start();
		t2.start();
	}
}

 

输出如下:

 

生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!
生产者生产了第:1个产品!
消费者消费了第:1个产品!

 

 

方法二:采用阻塞队列实现生产者消费者模式

阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。下面这是一个完整的生产者消费者代码例子,对比传统的wait、nofity代码,它更易于理解。

生产者:

class ProducerQueue  implements Runnable{

	private final BlockingQueue proQueue;
	
	
	public ProducerQueue(BlockingQueue proQueue) {
		this.proQueue = proQueue;
	}
	@Override
	public void run() {
		for(int i=0;i<10;i++){
			try {
			System.out.println("生产者生产的产品:"+i);
				proQueue.put(i);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

消费者:

class ConsumerQueue  implements Runnable{

	private final BlockingQueue conQueue;
	
	
	public ConsumerQueue(BlockingQueue conQueue) {
		this.conQueue = conQueue;
	}
	@Override
	public void run() {
		for(int i=0;i<10;i++){
			try {
				System.out.println("消费者消费的产品:"+conQueue.take());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

测试代码:

public class TestProducerAndConsumer2 {

	public static void main(String[] args) {
		BlockingQueue queue = new LinkedBlockingQueue(5);
		Thread t1 = new Thread(new ProducerQueue(queue),"生产者");
		Thread t2 = new Thread(new ConsumerQueue(queue),"消费者");
		t1.start();
		t2.start();
	}
}

这里使用LinkedBlockingDeque就是代替第一种方式里的Box

结果如下:

生产者生产的产品:0
生产者生产的产品:1
生产者生产的产品:2
消费者消费的产品:0
生产者生产的产品:3
消费者消费的产品:1
生产者生产的产品:4
消费者消费的产品:2
生产者生产的产品:5
消费者消费的产品:3
生产者生产的产品:6
消费者消费的产品:4
生产者生产的产品:7
消费者消费的产品:5
生产者生产的产品:8
消费者消费的产品:6
消费者消费的产品:7
生产者生产的产品:9
消费者消费的产品:8
消费者消费的产品:9

 

生产者消费者模式的好处

它的确是一种实用的设计模式,常用于编写多线程或并发代码。下面是它的一些优点:

  1.  它简化的开发,你可以独立地或并发的编写消费者和生产者,它仅仅只需知道共享对象是谁
  2. 生产者不需要知道谁是消费者或者有多少消费者,对消费者来说也是一样
  3.  生产者和消费者可以以不同的速度执行
  4.  分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码
    原文作者:JUC
    原文地址: https://blog.csdn.net/abc997995674/article/details/83310282
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞