一、生产者消费者模型
生产者消费者模型是一种并发编程模式,用于解决多线程环境下的数据共享和同步问题。在该模型中,有两类角色:生产者和消费者。
- 生产者:负责生成或生产数据,并将其放入一个共享的缓冲区(也称为队列)中。生产者可以是一个线程或进程。
- 消费者:负责从共享的缓冲区中获取数据,并对其进行处理或消耗。消费者可以是一个线程或进程。
以下是生产者消费者模型的基本工作流程:
- 创建一个共享的缓冲区作为交换数据的区域。
- 启动一个或多个生产者线程/进程,它们生成数据并将其放入缓冲区中。
- 启动一个或多个消费者线程/进程,它们从缓冲区中获取数据并进行处理。
- 当缓冲区为空时,消费者线程等待直到有数据可供消费。
- 当缓冲区已满时,生产者线程等待直到有空间可供放置数据。
- 生产者生成数据后,将其放入缓冲区,并唤醒任何等待中的消费者线程。
- 消费者从缓冲区获取数据后,对其进行处理,并唤醒任何等待中的生产者线程。
通过合理的同步机制(如互斥锁、条件变量等),生产者和消费者能够正确地协调工作,实现数据的有序生成和消费,避免数据竞争和死锁等并发问题。
生产者消费者模型在许多应用中都有广泛的应用,例如任务队列、消息队列、线程池等场景,可以有效地实现任务分配和资源利用的优化。
1、如何阻塞
- 线程自己阻塞自己,也就是生产者、消费者各自调用wait()和notify().
- 用一个阻塞队列,当获取不到或者放不进去的时候,入队、出队函数本身就是阻塞的。
2、如何实现双向通知
- wait()和notify()
- Condition机制
3、生产者消费者问题实现(wait和notify)
serviceImpl
public class KFCImpl implements IKFC {
private Queue<Food> queue = new LinkedBlockingQueue<>();
private final int MAX_SIZE = 10;
@Override
public synchronized void produce(String name) throws InterruptedException {
if (queue.size() >= MAX_SIZE) {
System.out.println("[生产者" + name + "] KFC生成达到上限,停止生成......");
wait();
} else {
Food food = new Food("上校鸡块");
queue.add(food);
System.out.println("[生产者" + name + "] 生成一个:" + food.getName() + ",KFC有食物:" + queue.size() + "个");
//唤醒等待的线程来消费
notifyAll();
}
}
@Override
public synchronized void consume(String name) throws InterruptedException {
if (queue.isEmpty()) {
System.out.println("[消费者" + name + "] KFC食物已空,消费者停止消费......");
wait();
} else {
Food food = queue.poll();
System.out.println("[消费者" + name + "] 消费一个:" + food.getName() + ",KFC有食物:" + queue.size() + "个");
//唤醒等待的线程来消费
notifyAll();
}
}
}
Consumer
public class ConsumerThread extends Thread{
private IKFC kfc;
public ConsumerThread(String name, IKFC kfc) {
this.kfc = kfc;
}
@Override
public void run() {
while(true){
try {
kfc.consume(getName());
sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
produce
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(getName());
sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}