生产者消费者问题

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

其特点大致如下:

  • 一组生产者生产数据到缓冲区中,另外一组消费者从缓冲区中取数据
  • 如果缓冲区已经满了,则生产者线程阻塞,等待队列有空间存放数据
  • 如果缓冲区为空,那么消费者线程阻塞,等待队列有数据进来
  • 生产者往缓冲去写入数据的同时,消费者可以同时拿取数据
  • 缓冲区数据先入先出

码农的世界里面向来是Talk is cheap, show me the code.

笔者写了一个小demo,缓冲区用的是BlockingQueue 接口的实现类,java.util.concurrent包下具有ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue,  PriorityBlockingQueue, SynchronousQueue.

示例中使用的是ArrayBlockingQueue, 一个数组实现的有界阻塞队列。有界也就意味着,它不能够存储无限多数量的元素。它有一个同一时间能够存储元素数量的上限。你可以在对其初始化的时候设定这个上限,但之后就无法对这个上限进行修改了。一旦初始化,大小就无法修改。

复制代码
package demo;

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;

public class Producer implements Runnable {

    private ArrayBlockingQueue<Integer> myQueue;
    private Random random = new Random();

    public ArrayBlockingQueue<Integer> getMyQueue() {
        return myQueue;
    }

    public void setMyQueue(ArrayBlockingQueue<Integer> myQueue) {
        this.myQueue = myQueue;
    }

    @Override
    public void run() {
        int tmp =-1;
        
        try {
            while (true) {
                long millis = (long) (Math.random() * 6000);
                // 模拟耗时计算
                Thread.sleep(millis);
                int element = random.nextInt(2000);
                myQueue.put(element);
                tmp = myQueue.size();
                String msg = Thread.currentThread().getName() + " is producing data : " + element;
                msg += "\r\n";
                msg += "Current queue size : " + tmp;
                System.out.println(msg);
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
Producer
复制代码
复制代码
package demo;

import java.util.concurrent.ArrayBlockingQueue;

public class Consumer implements Runnable {

    private ArrayBlockingQueue<Integer> myQueue;

    public ArrayBlockingQueue<Integer> getMyQueue() {
        return myQueue;
    }

    public void setMyQueue(ArrayBlockingQueue<Integer> myQueue) {
        this.myQueue = myQueue;
    }

    @Override
    public void run() {
        int element = -1;
        int tmp = -1;
        try {
            while (true) {
                long millis = (long) (Math.random() * 5000);
                // 模拟复杂计算
                Thread.sleep(millis);
                element = myQueue.take();
                tmp = myQueue.size();
                // 延迟一毫秒写日志
                Thread.sleep(1);
                String msg = Thread.currentThread().getName() + " is consuming data : " + element;
                msg += "\r\n";
                msg += "Current queue size : " + tmp;
                System.out.println(msg);
            }

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
Consumer
复制代码
复制代码
package demo;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

    private static final int SIZE = 10;
    private static ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(SIZE);
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        ExecutorService producerPool = Executors.newFixedThreadPool(3);
        ExecutorService consumerPool = Executors.newFixedThreadPool(2);
        
        Producer p1 = new Producer();
        p1.setMyQueue(queue);
        Producer p2 = new Producer();
        p2.setMyQueue(queue);
        Producer p3 = new Producer();
        p3.setMyQueue(queue);
        
        producerPool.execute(p1);
        producerPool.execute(p2);
        producerPool.execute(p3);
        
        Consumer c1 = new Consumer();
        c1.setMyQueue(queue);
        Consumer c2 = new Consumer();
        c2.setMyQueue(queue);
        
        consumerPool.execute(c1);
        consumerPool.execute(c2);
        
        
        //consumerPool.shutdown();
        //producerPool.shutdown();

    }

}
Main
复制代码

 

 

posted @   沐璟  阅读(158)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示