生产者消费者模型
一、引言
举个简单的例子,大家都寄过信吧,寄信的流程差不多是下面这样:
寄信流程就相当于一个简单的生产者消费者模型了
- 寄信人写好一封信——生产者制造数据
- 寄信人把信放入邮筒——生产者把数据放入缓冲区
- 邮递员把信从邮筒中取出——消费者把数据从缓冲区中取出
- 邮递员把信拿去邮局做相应的处理——消费者对数据进行处理
二、生产者消费者模型
-
简介
生产者消费者模型是一种常见的并发编程模型,用于解决生产者和消费者之间的数据交互问题。在该模型中,生产者负责生成数据并将其放入共享的缓冲区,而消费者则负责从缓冲区中取出数据并进行处理。
-
作用
为什么要使用生产者消费者模型呢?为什么不直接让生产者和消费者进行交互呢,而是中间多加一个缓冲区?
-
解耦
假如生产者和消费者分别是两个类。如果生产者直接调用消费者的方法,那么生产者就会对消费者产生依赖(耦合),之后要是消费者的方法代码发生改变,说不定会影响到生产者。而如果两者通过缓冲区进行交互,就可降低耦合。
-
异步(并发)
在不使用缓冲区的情况下,如果生产者生产一件产品完成,而没有消费者来取(处理数据太慢),生产者就不能生产下一个,直到消费者取走(处理完成),这大大降低生产者的生产效率。而使用了缓冲区,生产者每生产一个就可以往缓存里一丢,缓存一有东西消费者就可以来取,两者的工作互不影响,可以异步进行。
-
销峰(缓冲能力)
如果生产者制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉,由此可以平衡生产和消费的速度,提高系统的稳定性
-
三、代码模拟
以KFC生产消费汉堡为例。
定义一个Food类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Food {
private String name;
}
定义接口IKFC和它的实现类,以队列LinkedBlockingQueue作为中间的缓冲区,定义最大值MAX_SIZE并配合等待wait和唤醒notifyAll来限制缓存区的大小
public interface IKFC {
void produce() throws InterruptedException;
void consume() throws InterruptedException;
}
public class KFCImpl implements IKFC {
private Queue<Food> queue = new LinkedBlockingQueue<>();
private final int MAX_SIZE = 10;
@Override
public synchronized void produce() throws InterruptedException {
if (queue.size() >= MAX_SIZE) {
System.out.println("[生产者] KFC生成达到上限,停止生成......");
wait();
} else {
Food food = new Food("美味汉堡");
queue.add(food);
System.out.println("[生产者] 生成一个:"+food.getName()+",KFC有食物:"+ queue.size()+"个");
notifyAll();
}
}
@Override
public synchronized void consume() throws InterruptedException {
if(queue.isEmpty()){
System.out.println("[消费者] KFC食物已空,消费者停止消费......");
wait();
} else {
Food food = queue.poll();
System.out.println("[消费者] 消费一个:"+food.getName()+",KFC有食物:"+ queue.size()+"个");
notifyAll();
}
}
}
分别定义生产者和消费者线程
public class ProduceThread extends Thread{
private IKFC kfc;
public ProduceThread(String name, IKFC kfc) {
super(name);
this.kfc = kfc;
}
@Override
public void run() {
while (true) {
try {
kfc.produce();
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ConsumerThread extends Thread{
private IKFC kfc;
public ConsumerThread(String name, IKFC kfc) {
super(name);
this.kfc = kfc;
}
@Override
public void run() {
while (true) {
try {
kfc.consume();
sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试
public class Main {
public static void main(String[] args) {
IKFC kfc = new KFCImpl();
Thread p1 = new ProduceThread("A", kfc);
Thread p2 = new ProduceThread("B", kfc);
Thread p3 = new ProduceThread("C", kfc);
Thread c1 = new ConsumerThread("a", kfc);
Thread c2 = new ConsumerThread("b", kfc);
Thread c3 = new ConsumerThread("c", kfc);
Thread c4 = new ConsumerThread("d", kfc);
Thread c5 = new ConsumerThread("e", kfc);
p1.start();p2.start();p3.start();
c1.start();c2.start();c3.start();c4.start();c5.start();
}
}
测试结果