生产者消费者模式

  生产者消费者模式并不是JAVA23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程中最常用的一种设计模式

  在我们平时的开发场景中,例如某个线程是生产数据,另一个线程是处理数据,如果俩者的速度存在较大的差异,生产数据很快,处理数据很慢,那么每生产一笔数据之后都要等待数据处理完才能再次生产,则会造成资源的浪费;再或者生产数据的时候出问题了甚至会中断整个流程。但是如果引入一个缓冲区概念用到生产者消费者模式就不一样了。生产的数据直接放到缓冲区里,缓冲区满之后则不再生产,处理数据的时候直接从缓冲区里面拿数据。这样做的好处有如下:

  • 解耦,就像生产者消费者俩个齿轮本来直接连接,现在引入第三个齿轮放入他们之间,他们分别与这个齿轮连接,就算将来生产或者消费者出问题也不至于整个轮子都挂掉。

  • 支持忙闲不均,当生产者消费者俩者速度存在差异的时候,由于存在缓冲区,多生产的数据可以放在缓冲区里面等消费者慢慢来处理。例如现在的快递柜的存在,假设平常快递柜快递员可以一次拿走,但当双11的时候,快递柜爆满,则可以先放在柜子里面,等快递员分批消化。

  • 支持并发,防止程序阻塞,例如生产数据必须在消费者消费完后再生产效率低下。就像邮局寄信一样如果没有邮筒你寄信就只能等邮递员来取,万一路上堵车呢你就傻傻的等在那里。造成时间的浪费。

  好了废话不多说,直接撸代码,上文提到了缓冲区首先创建一个Box类当作缓冲区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* 生产者消费者模式当中的容器
* @author hspcadmin
*
*/
public class Box {
private final int MaxNum = 5;//最大数量

private static int CurrNum = 0;//当前数量

private static Box box = null;

private Box(){

}
/**
* 单例模式保证效率且线程安全
* @return
*/
public static Box getInstance(){
if(box==null){
init();
}
return box;
}
private static synchronized void init(){
if(box==null){
box = new Box();
}
}
/**
* 生产方法
*/
public synchronized void produce(){
while(CurrNum==MaxNum){
try {
System.out.println("仓库已满请等待消费者消费");
wait();//盒子满了之后等待消费者消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
CurrNum++;
System.out.println("当前线程:"+Thread.currentThread().getName() + "--生产者生产了一个商品,当前库存量:"+ CurrNum);
notifyAll();//通知消费者来消费
}
/**
* 消费方法
*/
public synchronized void consume(){
while(CurrNum<=0){
try {
System.out.println("仓库已空请等待生产者生产");
wait();//盒子空了之后等待生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
CurrNum--;
System.out.println("当前线程:"+Thread.currentThread().getName() + "--消费者消费了一个商品,当前库存量:"+ CurrNum);
notifyAll();//通知生产者生产
}
}

  接下来创建生产者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Producer extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +"***********生产者开始生产***********");
for(int j=0;j<=10;j++){
Box.getInstance().produce();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() +"***********生产者结束生产***********");
}
}

  消费者类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Consumer implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +"***********消费者开始消费***********");
for(int j=0;j<=8;j++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Box.getInstance().consume();
}
System.out.println(Thread.currentThread().getName() +"***********消费者结束消费***********");
}
}

  测试类以及结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Test {
public static void main(String args[]) throws Exception{
Producer p = new Producer();
Consumer c = new Consumer();
p.start();
new Thread(c);
Thread.sleep(2000);//为了使结果展示层次更加清楚
new Thread(c).start();
}
}
Thread-0***********生产者开始生产***********
当前线程:Thread-0--生产者生产了一个商品,当前库存量:1
当前线程:Thread-0--生产者生产了一个商品,当前库存量:2
当前线程:Thread-0--生产者生产了一个商品,当前库存量:3
当前线程:Thread-0--生产者生产了一个商品,当前库存量:4
Thread-2***********消费者开始消费***********
当前线程:Thread-0--生产者生产了一个商品,当前库存量:5
当前线程:Thread-2--消费者消费了一个商品,当前库存量:4
当前线程:Thread-0--生产者生产了一个商品,当前库存量:5
当前线程:Thread-2--消费者消费了一个商品,当前库存量:4
当前线程:Thread-0--生产者生产了一个商品,当前库存量:5
当前线程:Thread-2--消费者消费了一个商品,当前库存量:4
当前线程:Thread-0--生产者生产了一个商品,当前库存量:5
当前线程:Thread-2--消费者消费了一个商品,当前库存量:4
当前线程:Thread-0--生产者生产了一个商品,当前库存量:5
当前线程:Thread-2--消费者消费了一个商品,当前库存量:4
当前线程:Thread-0--生产者生产了一个商品,当前库存量:5
当前线程:Thread-2--消费者消费了一个商品,当前库存量:4
当前线程:Thread-0--生产者生产了一个商品,当前库存量:5
当前线程:Thread-2--消费者消费了一个商品,当前库存量:4
Thread-0***********生产者结束生产***********
当前线程:Thread-2--消费者消费了一个商品,当前库存量:3
当前线程:Thread-2--消费者消费了一个商品,当前库存量:2
Thread-2***********消费者结束消费***********