生产者消费者设计模式
生产者消费者设计模式学习
一、什么是生产者消费者设计模式
Java中的生产者-消费者设计模式是一种用于多线程编程的经典设计模式,它用于解决多个线程之间共享资源时的同步和通信问题。这个模式主要用在有数据生产者(Producer)和数据消费者(Consumer)的场景中,生产者负责产生数据,而消费者负责消费数据。
二、为什么会有这种模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。
在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。
为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。
三、是用来解决什么问题的
- 资源共享:在多线程环境中安全地共享资源。
- 数据同步:确保生产者不会在没有消费者的情况下过度填充缓冲区,反之亦然。
- 异步通信:允许生产者和消费者独立工作,但又能在适当的时候交互。
四、如何解决的
上个小demo
package com.ljh.producerconsumer; import java.util.LinkedList; import java.util.Queue; public class producerConsumerDemo { public static void main(String[] args) { Buffer buffer=new Buffer(); Consumer consumer=new Consumer(buffer); Producer producer=new Producer(buffer); producer.start(); consumer.start(); } } /** * 生产者类,继承自Thread,负责向缓冲区添加数据。 * 该类的实例将作为一个线程运行,不断地向缓冲区添加数据。 */ class Producer extends Thread { /** * 缓冲区对象,用于存储生产者产生的数据。 */ private Buffer buffer; /** * 构造函数,初始化生产者的缓冲区。 * * @param buffer 缓冲区实例,生产者将向这个缓冲区添加数据。 */ public Producer(Buffer buffer) { this.buffer = buffer; } /** * 重写run方法,定义生产者的线程执行逻辑。 * 生产者会循环10次,每次向缓冲区添加一个数字,并休眠1秒。 */ @Override public void run() { for (int i = 0; i < 10; i++) { try { // 向缓冲区添加数据。 buffer.add(i); // 休眠1秒,模拟生产数据的过程。 Thread.sleep(1000); } catch (InterruptedException e) { // 如果线程被中断,抛出运行时异常。 throw new RuntimeException(e); } } } } /** * 消费者类,继承自Thread,负责从缓冲区获取数据并消费。 */ class Consumer extends Thread { /** * 缓冲区对象,用于存储和提取数据。 */ private Buffer buffer; /** * 构造函数,初始化消费者类的缓冲区对象。 * * @param buffer 缓冲区对象,用于数据消费。 */ public Consumer(Buffer buffer) { this.buffer = buffer; } /** * 重写run方法,定义消费者线程的执行逻辑。 * 主要负责从缓冲区获取数据并打印,重复执行10次。 */ @Override public void run() { for (int i = 0; i < 10; i++) { int val = 0; try { // 从缓冲区获取数据,如果被中断则抛出RuntimeException。 val = buffer.pull(); } catch (InterruptedException e) { throw new RuntimeException(e); } // 打印消费了的数据。 System.out.println("消费了" + val); } } } /** * Buffer类用于实现一个固定大小的缓冲区。 * 它使用队列来存储数据,并且支持线程安全的添加和提取数据操作。 */ class Buffer { /** * 使用LinkedList作为队列实现,因为其在添加和删除操作上具有较好的性能。 */ private Queue<Integer> queue = new LinkedList<>(); /** * 缓冲区的大小被固定为5,这个大小决定了可以存储的最大数据量。 */ private int size = 5; /** * 向缓冲区中添加一个数据项。 * 如果缓冲区已满,则当前线程进入等待状态,直到缓冲区有空位。 * * @param val 要添加到缓冲区的值。 * @throws InterruptedException 如果线程在等待时被中断。 */ public synchronized void add(int val) throws InterruptedException { // 如果队列已满,则等待直到有空间可用。 if (queue.size() > size) { wait(); } // 添加新值到队列。 queue.add(val); // 唤醒其他等待的线程,可能有线程在等待消费数据。 notify(); } /** * 从缓冲区中提取一个数据项。 * 如果缓冲区为空,则当前线程进入等待状态,直到有数据可用。 * * @return 从缓冲区中提取的数据项。 * @throws InterruptedException 如果线程在等待时被中断。 */ public synchronized int pull() throws InterruptedException { // 如果队列为空,则等待直到有数据可用。 if (queue.size() == 0) { wait(); } // 提取并返回队列的第一个元素。 int val = queue.poll(); // 唤醒其他等待的线程,可能有线程在等待添加数据。 notify(); return val; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)