生产者消费式模型详解及相应的代码实现

本文详细介绍了生产者消费者模式的概念、优点及其实现方式。通过肯德基与麦当劳的订餐机制对比,生动解释了该模式如何解耦生产者与消费者,支持并发处理,平衡双方处理能力。并通过具体代码示例展示了模式的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

生产者消费者模式

定义

  生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。

优点

  • 解耦–生产者。消费者之间不直接通信,降低了耦合度。
  • 支持并发
  • 支持忙闲不均

实例

  肯德基与麦当劳的订餐机制对比: 在肯德基,顾客点单之后,点单员会把所点的食物完成封装之后拿来你面前,然后让你结账,有时候有些耗时操作没完成就会留下一个餐台号稍后送来。而在麦当劳的点餐模型大致是,你点完快餐之后要求你立即付款,付完款之后下一位点餐,而取餐的是在旁边等待,另一个服务员专责负责配餐。
 在并发模型中,肯德基比较倾向于一个线程把所有的服务都做完,而麦当劳倾向于服务解耦,让他们更专注于自己的业务。而肯德基的模型与BIO服务器的模型设计类似,麦当劳的模型则与生产者消费者模型十分相似。

实现代码

/**
 * @Author: zheng
 * @Description: 容量数据类型
 * @Date: 2020/8/25
 * @Version: 1.0
 */
public class Pcdata {
    private final int intData;

    public Pcdata(int intData) {
        this.intData = intData;
    }
    public Pcdata(String intData) {
        this.intData = Integer.parseInt(intData);
    }
    public int getIntData() {
        return intData;
    }
    @Override
    public String toString() {
        return "PCData{" +
                "intData=" + intData +
                '}';
    }
}
/**
 * @Author: zheng
 * @Description: 生产者线程
 * @Date: 2020/8/25
 * @Version: 1.0
 */
public class Producer implements Runnable {
    private volatile boolean isRunning = true;
    /**
     * 内存缓冲区
     */
    private final BlockingQueue<Pcdata> queue;
    /**
     * 总数 原子操作
     */
    private static final AtomicInteger count = new AtomicInteger();
    /**
     * 静态常量,睡眠时间(ms)
     */
    private static final int SLEEPTIME = 1000;

    public Producer(BlockingQueue<Pcdata> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        Pcdata data = null;
        Random random = new Random();
        System.out.println("start producting id:" + Thread.currentThread().getId());
        try {
            while (isRunning) {
                // 随机休眠时间
                Thread.sleep(random.nextInt(SLEEPTIME));
                data = new Pcdata(count.incrementAndGet());
                System.out.println(data + "加入队列");
                if (!queue.offer(data, 2, TimeUnit.SECONDS)) {
                    System.err.println("加入队列失败");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }

    public void stop() {
        isRunning = false;
    }
}
/**
 * @Author: zheng
 * @Description: 消费者线程
 * @Date: 2020/8/25
 * @Version: 1.0
 */
public class Consumer implements Runnable{
    /**
     * 缓冲队列
     */
    private final BlockingQueue<Pcdata> queue;
    /**
     * 静态常量,睡眠时间(ms)
     */
    private static final int SLEEPTIME = 1000;

    public Consumer(BlockingQueue<Pcdata> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        System.out.println("start Consumer id :" + Thread.currentThread().getId());
        Random random = new Random();
        try {
            while (true) {
                Pcdata pcdata = queue.take();
                if (pcdata!=null) {
                    int re = (int) Math.pow(pcdata.getIntData(), 2);
                    System.out.printf("%d*%d=%d\n", pcdata.getIntData(), pcdata.getIntData(), re);
                    Thread.sleep(random.nextInt(SLEEPTIME));
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }
}
/**
 * @Author: zheng
 * @Description: 主函数
 * @Date: 2020/8/25
 * @Version: 1.0
 */
public class Main {
    public static void main(String[] args) throws InterruptedException {
        // 阻塞队列(消息队列)
        BlockingQueue<Pcdata> queue = new LinkedBlockingDeque<>(10);
        Producer p1 = new Producer(queue);
        Producer p2 = new Producer(queue);
        Producer p3 = new Producer(queue);
        Consumer c1 = new Consumer(queue);
        Consumer c2 = new Consumer(queue);
        Consumer c3 = new Consumer(queue);
        ExecutorService pool = Executors.newCachedThreadPool();
        pool.execute(p1);
        pool.execute(p2);
        pool.execute(p3);
        pool.execute(c1);
        pool.execute(c2);
        pool.execute(c3);
        Thread.sleep(10*1000);
        p1.stop();
        p2.stop();
        p3.stop();
        Thread.sleep(3000);
        pool.shutdown();
    }
}

  注意:BlockingQueue是阻塞队列,一次只能被一个线程读取。如果用其他数据结构代替必须用synchronized([数据结构的实例对象])代替。最后说一下,欢迎大家留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值