生产者消费者模型

生产者消费者模型

一、引言

举个简单的例子,大家都寄过信吧,寄信的流程差不多是下面这样:

image-20231030191140392

寄信流程就相当于一个简单的生产者消费者模型了

  1. 寄信人写好一封信——生产者制造数据
  2. 寄信人把信放入邮筒——生产者把数据放入缓冲区
  3. 邮递员把信从邮筒中取出——消费者把数据从缓冲区中取出
  4. 邮递员把信拿去邮局做相应的处理——消费者对数据进行处理

二、生产者消费者模型

  1. 简介

    生产者消费者模型是一种常见的并发编程模型,用于解决生产者和消费者之间的数据交互问题。在该模型中,生产者负责生成数据并将其放入共享的缓冲区,而消费者则负责从缓冲区中取出数据并进行处理。

    image-20231030192146060

  2. 作用

    为什么要使用生产者消费者模型呢?为什么不直接让生产者和消费者进行交互呢,而是中间多加一个缓冲区?

    • 解耦

      假如生产者和消费者分别是两个类。如果生产者直接调用消费者的方法,那么生产者就会对消费者产生依赖(耦合),之后要是消费者的方法代码发生改变,说不定会影响到生产者。而如果两者通过缓冲区进行交互,就可降低耦合。

    • 异步(并发)

      在不使用缓冲区的情况下,如果生产者生产一件产品完成,而没有消费者来取(处理数据太慢),生产者就不能生产下一个,直到消费者取走(处理完成),这大大降低生产者的生产效率。而使用了缓冲区,生产者每生产一个就可以往缓存里一丢,缓存一有东西消费者就可以来取,两者的工作互不影响,可以异步进行。

    • 销峰(缓冲能力)

      如果生产者制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉,由此可以平衡生产和消费的速度,提高系统的稳定性

三、代码模拟

以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();
    }
}

测试结果

image-20231030200525193

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值