生产者消费者问题
生产者消费者模式是一个典型的并发编程需要考虑的问题, 通过一个中间容器来解决生产者和消费者的强耦合特性。 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力
其特点大致如下:
- 一组生产者生产数据到缓冲区中,另外一组消费者从缓冲区中取数据
- 如果缓冲区已经满了,则生产者线程阻塞,等待队列有空间存放数据
- 如果缓冲区为空,那么消费者线程阻塞,等待队列有数据进来
- 生产者往缓冲去写入数据的同时,消费者可以同时拿取数据
- 缓冲区数据先入先出
码农的世界里面向来是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(); } } }

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

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(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述