当前位置: 首页 > news >正文

生产者消费模式

  前言👀~

上一章我们介绍设计模式中的单例模式,今天我们来讲讲生产者消费模式

阻塞队列(重要)

生产者消费模式(重要)

阻塞队列在生产者消费模型中的作用

标准库的阻塞队列

手动实现阻塞队列


如果各位对文章的内容感兴趣的话,请点点小赞,关注一手不迷路,讲解的内容我会搭配我的理解用我自己的话去解释如果有什么问题的话,欢迎各位评论纠正 🤞🤞🤞

12b46cd836b7495695ce3560ea45749c.jpeg

个人主页:N_0050-CSDN博客

相关专栏:java SE_N_0050的博客-CSDN博客  java数据结构_N_0050的博客-CSDN博客  java EE_N_0050的博客-CSDN博客


阻塞队列(重要)

多线程代码中比较常用的一种数据结构

阻塞队列是一种特殊的队列,也遵守 "先进先出" 的原则,阻塞队列是一种线程安全的数据结构, 并且具有以下特性:

1.线程安全

2.带有阻塞特性

 1.如果队列为空,出队列就会发生阻塞,阻塞到其他队列往队列里添加元素为止

 2.如果队列为满,入队列就会发生堵塞,阻塞到其他队列从队列里取出元素为止

阻塞队列的最大作用,就是可以用来实现"生产者消费模型"(一种常见的多线程代码编写方式)


生产者消费模式(重要)

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

生产者消费模型例子:华莱士或者肯德基

生产者(厨师):负责做汉堡

消费者(顾客):负责吃汉堡

缓冲区(柜台或托盘):存放已经做好的汉堡,等待顾客来取

在这个例子中,厨师会不断生产汉堡放在柜台上,顾客会从柜台取汉堡。厨师和顾客的速度不同,需要有一个存放汉堡的地方(缓冲区)来协调生产和消费的速度。这就是生产者-消费者模型的典型场景。

如果厨师汉堡做的慢,顾客想吃就要等待(队列为空,出队列就会发生阻塞)这倒没什么问题。如果厨师汉堡做的很快都没地方放了,厨师就可以休息会,但是汉堡浪费了多出来了处理不掉(队列为满,入队列就会发生堵塞),这个会造成问题。

阻塞队列在生产者消费模型中的作用

1.阻塞队列也能使生产者和消费者之间解耦合

问题1:对于分布式系统来说,有很大意义,比如在一个分布式系统下,客户端给服务器发送请求,然后服务器A负责接收用户请求并且接收服务器B处理好的请求传回客户端服务器B负责处理用户请求并且将处理结果传回服务器A,此时服务器A和服务器B之间的耦合度就很高,如果其中一个服务器崩了,可能会影响到客户端这边的体验。此时如果加个服务器C,就要对服务器A代码做出改动和服务器C对接。但是这并不能实质性解决问题,还是可能会出现这样的问题

解决办法:在一个分布式系统下,此时客户端给服务器发送请求,然后服务器A还是负责接收用户请求,然后把请求丢到消息队列中,然后服务器B从消息队列中取出请求进行处理,处理后丢回消息队列,接着服务器A从队列中取出处理好的请求再传回客户端。这时候A和B之间没有联系就算服务器A或B出现问题,也不要紧,加个服务C就行了也不需要和服务器A进行对接修改代码啥的

阻塞队列也可能是个单独的服务器程序,把阻塞队列封装成单独的服务器程序部署到特定的机器上,这个时候把这个队列称为"消息队列"。消息队列应该是包含阻塞队列的,想让阻塞队列转变成消息队列,需要额外的实现一些功能。这个特定的机器上面有其他的功能,我们只是省下了功能,只不过是交给了机器去完成了。如果不进行封装,你想让阻塞队列变成消息队列,那么就需要咱们手动去加一些功能

2.阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力(想想618以及双11的情况下,阻塞队列的作用就很明显)也可以称削峰填谷,峰(短时间内请求量很多)谷(请求量少)

问题2:比如在一个分布式系统下,客户端给服务器发送了大量的请求,然后服务器A接收这些大量的请求立即发送给服务器B,所以A扛多少的访问量B也扛的也是一样。但是呢不同的服务器处理的业务不同,比如说服务器A就负责接收和发送请求,而服务器B呢不仅负责接收请求还要负责处理这些请求,在处理这些请求的时候,可能要去数据库拿数据再进行处理,服务器B呢可能处理不过来直接崩了,想想618以及双11的情况

解决办法:在一个分布式系统下,此时客户端给服务器发送了大量的请求,然后服务器A接收这些大量的请求,然后丢到消息队列中,然后服务器B从消息队列中取出请求进行处理,此时注意虽然有很大的请求需要处理,但是呢有消息队列扛着,服务器B可以按原先的速度处理这些请求,可能效率没那么高但是呢能保证会有响应,保证能对这些请求进行处理。总比直接把B搞崩了好,况且这种大量的请求场景持续的时间不久也就一下子,例如618和双11也就那一个时间点。所以这个消息队列在这种场景下能保证服务器能正常执行


标准库的阻塞队列

之前的Queue的方法,BlockingQueue也可以实现,但是这些方法没有阻塞的特点

1.基于数组

        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(100);

2.基于链表

        BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();

put方法阻塞式的入队列take方法阻塞式的出队列

public class Test {public static void main(String[] args) throws InterruptedException {BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();blockingQueue.put(1);blockingQueue.put(2);blockingQueue.put(3);System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());System.out.println(blockingQueue.take());}
}

输出结果


手动实现阻塞队列

基于一个普通队列加上线程安全、阻塞即可,这里使用基于数组(循环队列)手动实现阻塞队列

和之前我们实现循环队列一样,需要一个头来出元素,一个尾来放入元素,在这里只不过需要加入阻塞也就是我们的wait方法,以及保证线程安全加上锁,下面是代码的实现

class MyBlockingQueue {public int[] array;public int head;//头public int tail;//尾public int usedSize;public MyBlockingQueue() {array = new int[1000];}public void put(int val) throws InterruptedException {synchronized (this) {if (usedSize == array.length) {this.wait();}array[tail] = val;tail++;if (tail == array.length) {//放元素的时候要注意数组满了的情况tail要重置tail = 0;}usedSize++;this.notify();}}public int take() throws InterruptedException {synchronized (this) {if (usedSize == 0) {this.wait();}int value = array[head];head++;usedSize--;this.notify();return value;}}
}

上述代码这么写,不会出现什么问题,但是呢put方法如果是下面这样写的话可能会出现问题,什么问题呢?当一个线程使用notify方法或者interrupt方法来唤醒wait方法的时候,此时数组还是满的,然后唤醒后捕获到异常,它会接着往下执行,会把最后一个元素覆盖掉,并且usedSize++,这就出现问题了。之所以上面的代码不会出现问题是因为即使wait被唤醒,它会直接抛出异常然后线程退出。记住wait除了可以使用notify唤醒,还可以使用interrupt方法中断wait的状态

    public void put(int val) {synchronized (this) {if (usedSize == array.length) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}array[tail] = val;tail++;if (tail == array.length) {//放元素的时候要注意数组满了的情况tail要重置tail = 0;}usedSize++;this.notify();}}

解决办法:你可以在捕获异常后直接return返回,但是更推荐直接把if语句改成while循环,并且java官方也是推荐使用while循环的

    public void put(int val) {synchronized (this) {while (usedSize == array.length) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}array[tail] = val;tail++;if (tail == array.length) {//放元素的时候要注意数组满了的情况tail要重置tail = 0;}usedSize++;this.notify();}}

这样改后,就保守多了,不会出现上述提到的问题,但是注意可能会出现内存可见性问题,因为我们的变量又涉及到读又涉及到修改又涉及到比较的操作,谨慎为主给变量加上volatile关键字

    public volatile int head;//头public volatile int tail;//尾public volatile int usedSize;

完整阻塞队列代码

class MyBlockingQueue {public int[] array;public volatile int head;//头public volatile int tail;//尾public volatile int usedSize;public MyBlockingQueue() {array = new int[1000];}public void put(int val) throws InterruptedException {synchronized (this) {while (usedSize == array.length) {this.wait();}array[tail] = val;tail++;if (tail == array.length) {//放元素的时候要注意数组满了的情况tail要重置tail = 0;}usedSize++;this.notify();}}public int take() throws InterruptedException {synchronized (this) {while (usedSize == 0) {this.wait();}int value = array[head];head++;usedSize--;this.notify();return value;}}
}public class Test1 {public static int i = 0;public static void main(String[] args) {MyBlockingQueue blockingQueue = new MyBlockingQueue();Thread t1 = new Thread(() -> {while (true) {try {System.out.println("生产:" + i);blockingQueue.put(i);i++;Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();Thread t2 = new Thread(() -> {while (true) {try {System.out.println("消费:" + blockingQueue.take());} catch (InterruptedException e) {throw new RuntimeException(e);}}});t2.start();}
}

以上便是本章生产者消费模式的内容,在我们的日常开发中还是比较常见的,好好掌握,我们下一章还是讲解有关多线程的内容再见💕

相关文章:

生产者消费模式

前言&#x1f440;~ 上一章我们介绍设计模式中的单例模式&#xff0c;今天我们来讲讲生产者消费模式 阻塞队列&#xff08;重要&#xff09; 生产者消费模式&#xff08;重要&#xff09; 阻塞队列在生产者消费模型中的作用 标准库的阻塞队列 手动实现阻塞队列 如果各位对…...

PyMuPDF 操作手册 - 06 PDF的转换等

文章目录 七、转换 PDF 文档7.1 将pdf文本提取为 Markdown7.2 将pdf转换为word(使用`pdf2docx`库)7.2.1 安装pdf2docx7.2.2 转换所有页面7.2.3 转换指定页面7.2.4 多CPU核心处理7.2.5 转换加密的 pdf7.2.6 提取表格7.2.7 pdf2docx 和 python_docx 的关系7.3 PDF与图像的转换七…...

VUE3解决跨域问题

本文基于vue3 vite element-plus pnpm 报错&#xff1a;**** has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource. 原因&#xff1a;前端不能直接访问其他IP&#xff0c;需要用vite.config.ts &#xff0…...

2024阿里云大模型自定义插件(如何调用自定义接口)

1&#xff0c;自定义插件入口 2&#xff0c;插件定义&#xff1a;描述插件的参数 2.1&#xff0c;注意事项&#xff1a; 2.1.1&#xff0c;只支持json格式的参数&#xff1b;只支持application/JSON&#xff1b;如下图&#xff1a; 2.1.2&#xff0c;需要把接口描述进行修改&a…...

生成式人工智能将如何改变网络可访问性

作者&#xff1a;Matthew Adams 受 Be My Eyes 和 OpenAI 启发的一项实验&#xff0c;尝试使用 ChatGPT 4o 实现网页无障碍 在 Elastic&#xff0c;我们肩负着一项使命&#xff0c;不仅要构建最佳的搜索驱动型 AI 平台&#xff0c;还要确保尽可能多的人喜欢使用该平台。我们相…...

科普文:一文搞懂jvm实战(二)Cleaner回收jvm资源

概叙 在JDK9中新增了Cleaner类&#xff0c;该类的作用是用于替代finalize方法&#xff0c;更有效地释放资源并避免内存泄漏。 在JEP260提案中&#xff0c;封装了大部分Sun包内部的API之余&#xff0c;还引入了一些新的API&#xff0c;其中就包含着Cleaner这个工具类。Cleaner承…...

使用PyTorch高效读取二进制数据集进行训练

使用pickle制作类cifar10二进制格式的数据集 使用pytorc框架来训练&#xff08;以猫狗大战数据集为例&#xff09; 此方法是为了实现阿里云PAI studio上可视化训练模型时使用的数据格式。 一、制作类cifar10二进制格式数据 import os, cv2 from pickled import * from load_da…...

应急响应:应急响应流程,常见应急事件及处置思路

「作者简介」&#xff1a;冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础著作 《网络安全自学教程》&#xff0c;适合基础薄弱的同学系统化的学习网络安全&#xff0c;用最短的时间掌握最核心的技术。 这一章节我们需…...

Kotlin/Android中执行HTTP请求

如何在Kotlin/Android中执行简单的HTTP请求 okhttp官网 okhttp3 github地址 打开build.gradle.kts文件加入依赖 dependencies {implementation("com.squareup.okhttp3:okhttp:4.9.0") }在IDEA的Gradle面板点击reload按钮便会自动下载jar...

哈希表(C++实现)

文章目录 写在前面1. 哈希概念2. 哈希冲突3. 哈希函数4.哈希冲突解决4.1 闭散列4.1.1 线性探测4.1.2 采用线性探测的方式解决哈希冲突实现哈希表4.1.3 二次探测 4.2 开散列4.2.2 采用链地址法的方式解决哈希冲突实现哈希表 写在前面 在我们之前实现的所有数据结构中(比如&…...

深入理解代理模式(Proxy Pattern)及其实际应用

引言 在软件开发中&#xff0c;有时候我们需要在不改变现有代码的情况下添加一些功能&#xff0c;比如延迟初始化、访问控制、日志记录等。代理模式&#xff08;Proxy Pattern&#xff09;通过代理对象控制对原对象的访问&#xff0c;为现有代码添加了额外的功能。本篇文章将详…...

Elasticsearch (1):ES基本概念和原理简单介绍

Elasticsearch&#xff08;简称 ES&#xff09;是一款基于 Apache Lucene 的分布式搜索和分析引擎。随着业务的发展&#xff0c;系统中的数据量不断增长&#xff0c;传统的关系型数据库在处理大量模糊查询时效率低下。因此&#xff0c;ES 作为一种高效、灵活和可扩展的全文检索…...

【Python爬虫】Python爬取喜马拉雅,爬虫教程!

一、思路设计 &#xff08;1&#xff09;分析网页 在喜马拉雅主页找到自己想要的音频&#xff0c;得到目标URL&#xff1a;https://www.ximalaya.com/qinggan/321787/ 通过分析页面的网络抓包&#xff0c;最终的到一个比较有用的json数据包 通过分析&#xff0c;得到了发送json…...

基于Jmeter的分布式压测环境搭建及简单压测实践

写在前面 平时在使用Jmeter做压力测试的过程中&#xff0c;由于单机的并发能力有限&#xff0c;所以常常无法满足压力测试的需求。因此&#xff0c;Jmeter还提供了分布式的解决方案。本文是一次利用Jmeter分布式对业务系统登录接口做的压力测试的实践记录。按照惯例&#xff0…...

IDEA常用代码模板

在 IntelliJ IDEA 中,常用代码模板可以帮助你快速生成常用的代码结构和模式。以下是一些常用的代码模板及其使用方法: 动态模板(Live Templates) psvm:生成 public static void main(String[] args) 方法。sout:生成 System.out.println(); 语句。soutv:生成 System.ou…...

基于大语言模型的多意图增强搜索

随着人工智能技术的蓬勃发展&#xff0c;大语言模型&#xff08;LLM&#xff09;如Claude等在多个领域展现出了卓越的能力。如何利用这些模型的语义分析能力&#xff0c;优化传统业务系统中的搜索性能是个很好的研究方向。 在传统业务系统中&#xff0c;数据匹配和检索常常面临…...

【ai】ubuntu18.04 找不到 nvcc --version问题

nvcc --version显示command not found问题 这个是cuda 库: windows安装了12.5 : 参考大神:解决nvcc --version显示command not found问题 原文链接:https://blog.csdn.net/Flying_sfeng/article/details/103343813 /usr/local/cuda/lib64 与 /usr/local/cuda-11.3/lib64 完…...

深入了解DDoS攻击及其防护措施

深入了解DDoS攻击及其防护措施 分布式拒绝服务&#xff08;Distributed Denial of Service&#xff0c;DDoS&#xff09;攻击是当今互联网环境中最具破坏性和普遍性的网络威胁之一。DDoS攻击不仅危及企业的运营&#xff0c;还可能损害其声誉&#xff0c;造成客户信任度的下降。…...

【面试系列】产品经理高频面试题及详细解答

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&#xff1a;详细讲解AIGC的概念、核心技术、…...

前端特殊字符数据,后端接收产生错乱,前后端都需要处理

前端&#xff1a; const data {createTime: "2024-06-11 09:58:59",id: "1800346960914579456",merchantId: "1793930010750218240",mode: "DEPOSIT",channelCode: "if(amount > 50){iugu2pay;} else if(amount < 10){iu…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...