当前位置: 首页 > 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…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...