生产者消费者模型【新版】

article/2025/9/28 7:20:39

目录

啥是生产者消费者模型?

生产者消费者模型存在问题??如何进行解决呢??

生产者消费者模型导致的问题

什么是阻塞队列

生产者消费者模型优点

生产者消费者模型实现

Message

MessageQueue

获取消息get方法

生产消息take方法

测试生产者消费者模型


啥是生产者消费者模型?

生产者消费者模型是多线程的一个经典案例,在多个线程并发执行时,会有线程来生产数据这就是生产者,同时还有线程来消费数据这是消费者,生产者和消费者共同完成多线程并发任务.

生产者消费者模型存在问题??如何进行解决呢??

生产者消费者模型导致的问题

  • 生产者和消费者在进行协作时必然会出现一些问题,比如生产者生产数据过快,而消费者消费数据过慢就导致生产者会处于阻塞等待,同理消费者消费数据过快,而生产者生产过慢,就会导致生产者会阻塞等待.
  • 生产者和消费者在进行协作时,如果有一个出现异常,就会影响另外一个.

如何理解 "解耦合" ???

耦合性比较高 -- 两者关联性非常大的

耦合性比较低 -- 两者关联性较低 ---- 日常开发中我们要遵循 "高内聚","低耦合"

所谓高内聚 : 进行分类,让其更加几种管理,分模块

左图所示 :

两个服务器之间直接进行交互,这就是耦合性高的体现,如果服务器A出现异常(或者挂掉)就会导致服务器B挂掉,同理服务器B出现异常或者挂掉,对服务器A也有影响

有图所示:

两个服务器之间不直接进行交互,这是耦合性低的体现.一个服务器异常不会影响另外一个服务器.两个服务器依靠阻塞队列进行交互

如何理解"削峰减谷" 或者 如何理解"平衡生产者和消费者的处理能力" ???

对于左图:

如果大量客户端在同一时刻一起访问服务器A,这就造成服务器A崩掉,也会影响到服务器B也崩掉

对于有图:

如果大量客户端同一时刻访问服务器A,服务器A会将数据全部放到阻塞队列中,服务器B以正常的方式与阻塞队列进行交互,将其压力给阻塞队列,不会让其服务器一次性的就崩了.

什么是阻塞队列

阻塞队列:阻塞队列能够保证线程安全-------最重要的应用场景: 生产者消费者模型

满足:

  • 当队列满时,尝试入队列,会进行阻塞
  • 当队列空时,尝试出对列,会进行阻塞

生产者消费者模型优点

  • 阻塞队列能使得生产者和消费者之间的解耦
  • 阻塞队列相当于缓冲区,平衡生产者和消费者的处理能力

生产者消费者模型实现

生产者消费者模式与保护性暂停模式有区别:

  • 保护性暂停模式需要生产结果的线程与消费结果的线程要一一对应的..
  • 而生产者消费者模式不需要两个线程一一对应,可以理解为异步,产生结果和接收结果有延迟.结果产生了并不是立即拿到.

我们这个生产者消费者模型使用一个队列(可以称为消息队列) 来实现的,这个消息队列对生产者和消费者进行解耦,来达到平衡生产者和消费者的线程资源.(平衡生产者和消费者的处理能力)

生产者只负责产生结果数据,不关心数据如何去处理,消费者只负责专心消费结果数据,不需要关心数据如何生产的.

这个消息队列有容量限制:

  • 当队列满的时候,生产者就不会在加入数据,会阻塞住,就会等待消费者来消费数据.然后通知生产者生产数据.
  • 当队列为空的时候,消费者不会在消费数据,会阻塞住,等待生产者生产数据之后通知消费者.

JDK中的阻塞队列就是基于生产者消费者模式来实现的

Message

/*** 消息->不能被子类覆盖, 只有get方法->只能获取到消息id/val*/
public final class Message {private int id;private String val;public Message(int id, String val) {this.id = id;this.val = val;}public int getId() {return id;}public String getVal() {return val;}
}

这个Message属于消息类,也就是生产者和消费者传递的消息.

这里特意加了final表示不能有子类,当然也不能被子类重新,也就是说生产者和消费者传递的消息是不可以被改变的.

同时也去掉了这个Message类的set方法,也表示不能修改整个消息

MessageQueue

@Slf4j(topic = "c.MessageQueue")
public class MessageQueue {//用一个集合来生产和消费消息-->从尾部生产,从头部消费private LinkedList<Message> list = new LinkedList<>();//消息队列容量private int capacity;public MessageQueue(int capacity) {this.capacity = capacity;}//获取消息-->消费者public Message get(){//如果消息队列为空就一直等待不能消费synchronized (list) {while(list.isEmpty()){try {log.debug("消息队列已空,消费者阻塞");list.wait();} catch (InterruptedException e) {e.printStackTrace();}}//消费掉Message message = list.removeFirst();log.debug("已消费消息");list.notifyAll();return message;}}//存入消息-->生产者public void take(Message message){synchronized (list) {while(list.size()==capacity) {try {log.debug("消息队列已满,生产者阻塞");list.wait();} catch (InterruptedException e) {e.printStackTrace();}}list.addLast(message);log.debug("已生产消息");list.notifyAll();}}
}

MessageQueue成员解析

//用一个集合来生产和消费消息-->从尾部生产,从头部消费
private LinkedList<Message> list = new LinkedList<>();
//消息队列容量
private int capacity;
//为整个消息队列初始化容量
public MessageQueue(int capacity) {this.capacity = capacity;
}

获取消息get方法

public Message get(){//如果消息队列为空就一直等待不能消费synchronized (list) {while(list.isEmpty()){try {log.debug("消息队列已空,消费者阻塞");list.wait();} catch (InterruptedException e) {e.printStackTrace();}}//消费掉Message message = list.removeFirst();log.debug("已消费消息");list.notifyAll();return message;}
}

get方法由消费者执行,消费者来获取消息,也就是消费消息.

因为生产者和消费者不止一个,也就是多个线程,所以肯定会有线程安全问题->使用的集合不是线程安全的.

所以我们就要为线程共享的作为锁对象,这里拿list作为锁对象. 为它加锁.

while(list.isEmpty()){try {log.debug("消息队列已空,消费者阻塞");list.wait();} catch (InterruptedException e) {e.printStackTrace();}
}

消费者先要判断队列是否为空,如果队列为空就不进行消费了,就等待,等待生产者生产完消息之后通知消费者继续消费.

//消费掉
Message message = list.removeFirst();
log.debug("已消费消息");
list.notifyAll();
return message;

否则的话,如果队列不为空,消费者就可以消费消息了,消费完消息就通知生产者可以生产消息了.

生产消息take方法

//存入消息-->生产者
public void take(Message message){synchronized (list) {while(list.size()==capacity) {try {log.debug("消息队列已满,生产者阻塞");list.wait();} catch (InterruptedException e) {e.printStackTrace();}}list.addLast(message);log.debug("已生产消息");list.notifyAll();}
}

生产消息的方法由生产者执行,,首先还是对多个线程共享的队列上锁.

  • 生产者这边先要判断队列是否满了,如果满了,生产者就不在生产消息,进行wait等待
  • 如果队列没有满,极具生产消息,生产完消息之后就通知消费者可以消费消息了

测试生产者消费者模型

public class TestMain {public static void main(String[] args) throws InterruptedException {MessageQueue queue = new MessageQueue(2);//生产者线程for(int i =0;i<3;++i) {int id = i;new Thread(()->{queue.take(new Message(id,"message" + id));},"生产者" + i).start();}Thread.sleep(1000);//消费者线程-->消费者不断地消费new Thread(()->{while(true) {Message message = queue.get();}},"消费者").start();}
}
  • 首先创建容量为2的阻塞队列.
  • 创建三个生产者线程来生产消息.
  • 创建一个消费者线程不断地消费消息.

我们看执行结果.

先是生产者0和生产者1进行生产消息,然后当生产2生产消息的时候,队列就已经满了,生产者阻塞住,接下来就会通知消费者消费消息,当消费了2个消息之后,在消费一个消息时,队列为空,就会阻塞住,因为刚才生产者2线程阻塞住了,这时消费者那边通知队列为空,所以生产者2在生产一个消息,消费者消费完之后在消费,队列就为空了,阻塞住了.

因为就创建3个线程来生产消息,所以最终就阻塞住,没有消息生产了,消费者也就不能消费消息了


http://chatgpt.dhexx.cn/article/RCNpAEWl.shtml

相关文章

【Linux】生产者消费者模型

文章目录 一. 什么是生产者消费者模型1. 基本概念2. 三种关系3. 再次理解生产者消费者模型 二. 生产者消费者模型的优点三. 基于BlockingQueue的生产者消费者模型1. 准备工作2. 阻塞队列实现3. 测试阻塞队列4. 阻塞队列完整代码5. 关于改进阻塞队列的几点补充5.1 多生产者多消费…

生产者与消费者模型

1、【什么是生产者与消费者模型呢&#xff1f;】 一种重要的模型&#xff0c;基于等待/通知机制。生产者/消费者模型描述的是有一块缓冲区作为仓库&#xff0c;生产者可将产品放入仓库&#xff0c;消费者可以从仓库中取出产品&#xff0c;生产者/消费者模型关注的是以下几个点&…

生产者消费者模型——C语言代码详解

概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用等待消费者处理&#xff0c;直接扔给阻塞队列&#xff0c;消费者不找生产者要数据…

【Java总结】生产者消费者模型

生产者消费者模型主要结构如下&#xff0c;是一个典型的线程同步的案例。下面就来使用java做几种线程同步的方式来实现以下该模型 确保一个生产者消费者模型的稳定运行的前提有以下几个 生成者应该具备持续生成的能力消费者应该具备持续消费的能力生产者的生成和消费消费有一定…

【设计模式】生产者消费者模型

带你轻松理解生产者消费者模型&#xff01;生产者消费者模型可以说是同步与互斥最典型的应用场景了&#xff01;文末附有模型简单实现的代码&#xff0c;若有疑问可私信一起讨论。 文章目录 一&#xff1a;为什么要使用生产者消费者模型&#xff1f;二&#xff1a;生产者消费者…

模拟生产者消费者模型

生产者消费者是多线程很经典的一个模型 牵涉三个对象&#xff1a;仓库、生产者、消费者 仓库代表共享变量 生产者表示在仓库生产货物 消费者表示从仓库拿出货物 实现思路&#xff1a;利用synchronizedwait()notify() 对生产者消费者对应的操作用synchronized关键字保证线程安全…

生产者消费者模型java实现

做题的时候遇到了生产者消费者问题&#xff0c;这个问题可以说是线程学习的经典题目了&#xff0c;就忍不住研究了一波。它描述是有一块缓冲区&#xff08;队列实现&#xff09;作为仓库&#xff0c;生产者可以将产品放入仓库&#xff0c;消费者则可以从仓库中取走产品。在Java…

生产者消费者模型详解以及实现

生产者消费者模式 我们先来看看什么是生产者消费者模式&#xff0c;生产者消费者模式是程序设计中非常常见的一种设计模式&#xff0c;被广泛运用在解耦、消息队列等场景。在现实世界中&#xff0c;我们把生产商品的一方称为生产者&#xff0c;把消费商品的一方称为消费者&…

Java生产者消费者模型的五种实现方式

转自&#xff1a;https://juejin.im/entry/596343686fb9a06bbd6f888c 前言 生产者和消费者问题是线程模型中的经典问题&#xff1a;生产者和消费者在同一时间段内共用同一个存储空间&#xff0c;生产者往存储空间中添加产品&#xff0c;消费者从存储空间中取走产品&#xff0c…

生产者消费者模型---详解及代码实现

概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用等待消费者处理&#xff0c;直接扔给阻塞队列&#xff0c;消费者不找生产者要数据…

生产消费者模型

生产消费者模型中包含三个部分&#xff0c;生产者、消费者和交易场所。其中涉及如下的关系&#xff1a; &#xff08;1&#xff09;生产者和生产者之间的关系&#xff1a;由于生产者的生产面向的都是交易场所&#xff0c;所以生产者之间是存在竞争关系的&#xff0c;就像一家超…

生产者-消费者模型

什么是生产者消费者模型 生产者 - 消费者模型&#xff08; Producer-consumer problem&#xff09; 是一个非常经典的多线程并发协作的模型&#xff0c;在分布式系统里非常常见。 这个模型由两类线程和一个缓冲区组成来组成 生产者线程&#xff1a;生产数据&#xff0c;并把…

最长上升子序列和最长公共子序列

文章目录 文章目录 文章目录一、基本知识二、最长上升子序列1.朴素版2.二分版 三、最长公共子序列 一、基本知识 1.子串和子序列的区别&#xff1a; 子串必须连续,子序列可以不连续。 2.最长上升子序列(LIS)&#xff1a; 是指一个序列中最长的单调递增的子序列。 3.最长公共…

求最长子序列及回溯

D - 最长公共子序列问题 Description 给定两个序列 X{x1,x2,…,xm} 和 Y{y1,y2,…,yn}&#xff0c;找出X和Y的最长公共子序列。 Input 输入数据有多组&#xff0c;每组有两行 &#xff0c;每行为一个长度不超过500的字符串&#xff08;输入全是大写英文字母&#xff08;A,Z…

算法14-求最长子序列

题目&#xff1a; 给定数组arr。求最长的有序子序列的长度&#xff0c;返回长度int 分析&#xff1a; 1 要求的子串是有序的&#xff0c;就要比大小 2 用最暴力大方法&#xff0c;看成窗口问题&#xff0c;每一个元素求出它左边的最长序列子串&#xff0c;写入一个数组dp&…

最长子序列——动态规划

动态规划算法通常用于求解具有某种最优性质的问题。动态规划算法与分治法类似&#xff0c;其基本思想也是将待求解问题分解成若干个子问题&#xff0c;先求解子问题&#xff0c;然后从这些子问题的解得到原问题的解。与分治法不同的是&#xff0c;适合于用动态规划求解的问题&a…

最长公共子串与最长子序列

一 序 本文属于《图解算法》系列&#xff0c;上一篇整理了动态规划&#xff0c;动态规划可以帮助我们解决给定约束条件下找到最优解&#xff0c;例如背包问题。 在问题可分解为彼此独立且离散的子问题时&#xff0c;就可使用动态规划来解决。 在看个例子&#xff0c;求两个字…

动态规划:最长子序列

最长公共子序列&#xff1a; 链接&#xff1a;https://www.nowcoder.com/questionTerminal/9ae56e5bdf4f480387df781671db5172 题目&#xff1a; 我们有两个字符串m和n&#xff0c;如果它们的子串a和b内容相同&#xff0c;则称a和b是m和n的公共子序列。子串中的字符不一定在原字…

C++最长子序列

LeedCode-300. 最长上升子序列 LeetCode-300. 最长上升子序列 方法一&#xff1a;O(n^2)可能会超时&#xff1b;方法二&#xff1a;贪心二分法&#xff0c;使用lower_bound()&#xff1b; 下面是贪心二分算法&#xff1a; 由于要得到最长的递增子序列&#xff0c;就要让序列…

数组:最长子序列问题四种解法

数组&#xff1a;最长子序列问题四种解法 问题描述&#xff1a; 给定一个整数数组 nums &#xff0c;找到一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 示例 1 : 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子…