【JavaEE】生产者消费者模式

作者主页:paper jie_博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将MySQL基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等

内容分享:本期将会分享设计模式中的生产者消费者模式

目录

什么是阻塞队列

什么是生产者消费者模式

生产者消费者模式的特点

 Java标准库中的阻塞队列

自定义实现一个阻塞队列

普通队列

 实现线程安全

正解

实现阻塞

正解

 基于自己实现的阻塞队列实现一个简单的生产者消费者模型


什么是阻塞队列

阻塞队列它也是队列,遵守着先进先出的原则.且它是一种线程安全的数据结构.它有两个特性:

1. 当队列满的时候,继续入队就会进行堵塞,直到有其他线程从队列中拿走元素才会解除堵塞.

2. 当队列为空的时候,继续出队就会进行堵塞,直到有其他线程从队列中插入元素后才会解除堵塞.

我们的阻塞队列最经典的使用场景就是生产者消费者模式.

举个栗子:

在我们家中,捏饺子一般有两个步骤: 1. 捏饺子皮, 2.包饺子. 假设有三个人.一个滑稽捏饺子皮,其他两个滑稽包饺子. 滑稽捏好饺子皮厚后就会放到板子上,而其他的滑稽就不用直接去捏饺子皮的滑稽手上拿饺子皮,而是去板子上拿即可.这样他们直接就只需要关注这个板子即可. 而这个板子就充当了我们的阻塞队列.

什么是生产者消费者模式

生产者消费者模式就是基于一个中间容器来解决它们之间的强耦合性问题.而这个中间容器就是阻塞队列.加入阻塞队列后,生产者与消费者之间就不会直接联系,而是通过通过阻塞队列来进行数据传输.这样生产者生产数据就不用知道是谁来处理他的数据,不用等待,直接交给阻塞队列即可.而消费者也不会去找生产者要数据,而是去阻塞队列里拿.

生产者消费者模式的特点

1. 通过阻塞队列可以降低生产者与消费者之间的耦合性

假设有三个服务器ABC,BC是将数据处理到,A是接受它们的数据.如果在没有加入阻塞队列的情况下.可能就会发生: 当B或者C挂了,这可能就会导致A也会挂,它们之间是强耦合的关系.因为B或者C的操作中需要涉及到一些关于A的操作.而A的操作也会涉及到一些关于B或C的操作.

但是当加入阻塞队列后就会发生不一样的结果. C,B处理好的结果只需要放到阻塞对列中,而A也只需要去阻塞队列中拿即可.这样B,C的操作对于A的影响就会很小,从而降低了他们之间的耦合性.

 

2. 削峰填谷 

这里就是加入阻塞队列起到一个平衡生产者与消费者之间的处理能力, 加入阻塞队列就可以防止当生产者一下生产出大量数据,而消费者一时间消费不了而导致挂了的问题.

举个栗子:

假设一个场景: 客户端发出请求,服务A接受请求,然后将请求交给BC处理器进行逻辑处理. 在正常情况下处理器是可以及时处理的.但是在一些特殊的时候会有一些突发峰值.外界客户端的请求非常的多.A接受这些请求一下子全部交给B,C服务器来处理,它们一下子可能就会支撑不住. 因为B,C需要就行逻辑处理业务,需要的资源开销就会比较大.如果一下给它大量的请求进行处理,处理器的资源可能就会超过它的上限而导致机器挂了.

但是在加入阻塞队列后就不用担心这种情况了. 就算客户端有大量的请求,A接受后也是传送给阻塞队列,再由B,C去阻塞队列中拿数据处理来慢慢消化.这就算数据再多,只要A服务器,阻塞队列不挂(阻塞队列和A服务器抗压能力很强,它们只需要进行存储数据和传送数据),BC也可以按正常速度进行处理.

 Java标准库中的阻塞队列

在Java标准库中也提供了阻塞队列,如果我们需要使用阻塞队列,只需要使用标准库中的即可. 库中的阻塞队列叫BlockingQueue,它是一个interface接口,它实现的的类有:

ArrayBlockingQueue

LinkedBlockingQueue

priorityBlockingQueue

里面的put和offer方法就是入队方法,但是put是带有阻塞的功能. take也是出队方法,但它也带有阻塞的功能.

public class ThreadDemo1 {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);queue.put("aaa");System.out.println(queue.take());System.out.println(queue.take());}
}

自定义实现一个阻塞队列

这里我们准备实现一个基于数组的阻塞队列,也就是环形队列.这里队列里面我们需要一个数组,计数器和两个头尾指针,put和take方法. 这里我们分三步来实现这个阻塞队列.

1) 普通队列

2) 实现线程安全

3) 实现阻塞功能

普通队列

class MyarrayBlockingQueue {private int elems[] = null;private int size = 0;private int head = 0;private int tail = 0;public MyarrayBlockingQueue(int capactiy) {elems = new int[capactiy];}public void put(int value) {//判断队列满没满if(size == elems.length) {//阻塞return;}//添加元素elems[tail] = value;tail++;//判断尾指针是不是需要循环到0位置if(tail == elems.length) {tail = 0;}size++;}public int take() {int elem = 0;//判断队列为不为空if(size == 0) {//阻塞return elem;}//出队elem = elems[head];head++;if(head == elems.length) {head = 0;}size--;return elem;}
}

 实现线程安全

实现线程安全,我们就需要加锁.但是当我们下面这个代码这样加锁时,就会出现问题.

public void put(int value) {//判断队列满没满if(size == elems.length) {//阻塞return;}synchronized (this) {//添加元素elems[tail] = value;tail++;//判断尾指针是不是需要循环到0位置if(tail == elems.length) {tail = 0;}size++;}}public int take() {int elem = 0;//判断队列为不为空if(size == 0) {//阻塞return elem;}synchronized(this) {//出队elem = elems[head];head++;if(head == elems.length) {head = 0;}size--;return elem;}}

我们发现如果当有两个线程t1,t2同时使用put或者take方法.假设当前有99个元素,容量为100.当t1执行到if(size == elems.length)后被调度走了,再轮到t2执行.当t2执行完后,队列的元素语已经满了.但是当轮到t1执行时因为上一次的if判断它还会再入队一个元素,这就会出现size为101的问题.

正解

我们将if判断条件也放到锁中就可以了.

public void put(int value) {synchronized (this) {//判断队列满没满if(size == elems.length) {//阻塞return;}//添加元素elems[tail] = value;tail++;//判断尾指针是不是需要循环到0位置if(tail == elems.length) {tail = 0;}size++;}}public int take() {int elem = 0;synchronized(this) {//判断队列为不为空if(size == 0) {//阻塞return elem;}//出队elem = elems[head];head++;if(head == elems.length) {head = 0;}size--;return elem;}}

实现阻塞

这里需要实现阻塞就要使用我们的wait和notify方法.

当队列满时,使用put就会执行wait进入阻塞,只有当使用take调用notify队列才会解除堵塞.

当队列为空时,使用take就会执行wait进入堵塞,只有当使用put调用notify队列才会解除堵塞.

public void put(int value) throws InterruptedException {synchronized (this) {//判断队列满没满if(size == elems.length) {this.wait();return;}//添加元素elems[tail] = value;tail++;//判断尾指针是不是需要循环到0位置if(tail == elems.length) {tail = 0;}size++;this.notify();}}public int take() throws InterruptedException {int elem = 0;synchronized(this) {//判断队列为不为空if(size == 0) {this.wait();return elem;}//出队elem = elems[head];head++;if(head == elems.length) {head = 0;}size--;this.notify();return elem;}}

但是这样又会出现一个问题. 假设有三个线程t1,t2,t3 t1和t2调用put. t3调用take. 且这个队列已经满了. 这里就会有一种情况: t1和t2调用put发现满了就都会在wait那里堵塞,且解锁. 这时t3就执行take方法出队了一个且调用了notify方法唤醒了t1. 则t1也就向下执行入队了一个,此时队列是满了.但是!!!t1的notify方法就可能会唤醒t2.而t2就会直接向下执行又入队一个,但队列是满的,这就出现问题了.

正解

我们可以在if判断那里将if改成while,这样就算被notify唤醒了,也会再次判断队列是不是满/空.才会选择是不是执行还是继续堵塞.

public void put(int value) throws InterruptedException {synchronized (this) {//判断队列满没满while(size == elems.length) {this.wait();return;}//添加元素elems[tail] = value;tail++;//判断尾指针是不是需要循环到0位置if(tail == elems.length) {tail = 0;}size++;this.notify();}}public int take() throws InterruptedException {int elem = 0;synchronized(this) {//判断队列为不为空while(size == 0) {this.wait();return elem;}//出队elem = elems[head];head++;if(head == elems.length) {head = 0;}size--;this.notify();return elem;}}

 基于自己实现的阻塞队列实现一个简单的生产者消费者模型

public class ThreadDemo8 {public static void main(String[] args) {block queue = new block(1000);Thread t1 = new Thread(() -> {int count = 0;while(true) {try {queue.put(count);System.out.println("生产者: " + count);Thread.sleep(1000);count++;} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 = new Thread(() -> {while(true) {try {System.out.println("消费者: " +  queue.take());} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/204380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

期末速成数据库极简版【分支循环函数】(4)

目录 全局变量&局部变量 局部变量定义declare 局部变量赋值select 局部变量赋值select 【1】分支结构IF 【2】分支结构CASE 简单CASE语句 搜索CASE语句 【3】循环结构While 【4】系统函数 常用字符串函数 时间函数 【5】自定义函数—标量函数 函数创建 函…

如何学习Java并发编程

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 2012年我刚转行到互联网…

定义一个学生类,其中有3个私有数据成员学号、姓名、成绩,以及若于成员。 函数实现对学生数据的赋值和输出。

#include <stdio.h> // 定义学生类 typedef struct Student { int stuNum; // 学号 char name[20]; // 姓名&#xff0c;假设最长为20个字符 float score; // 成绩 } Student; // 初始化学生信息 void initializeStudent(Student *student, int num, const…

Office Tool Plus 使用教程 让个人也能轻松使用上免费的Office

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

接雨水-困难

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a;输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表…

忘记PDF密码了,怎么办?

PDF文件有两种密码&#xff0c;一个打开密码、一个限制编辑密码&#xff0c;因为PDF文件设置了密码&#xff0c;那么打开、编辑PDF文件就会受到限制。忘记了PDF密码该如何解密&#xff1f; PDF和office一样&#xff0c;可以对文件进行加密&#xff0c;但是没有提供恢复密码的功…

优雅草蜻蜓I即时通讯·水银版私有化部署之安卓Android端编译-02

Android 项目配置 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 使用以上Android studio版本 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 下载最低sdk最低版本28 完成后就可以导入项目(项目导入不能开VPN,会导致部分三方库…

一v一聊天

服务端 package 一对一用户;import java.awt.BorderLayout; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Vector;…

使用加密工具The Enigma Protector ,快速保护您的软件安全

我们多次被问到使用Enigma Protector保护软件免遭破解和逆向工程的最佳方法是什么&#xff1f;在这里我将解释保护常用应用程序的技巧是什么。 许多开发人员认为&#xff0c;如果他们只需单击“保护”按钮&#xff0c;保护程序就会自动完成所有操作&#xff0c;无需嵌入额外的…

【Java基础系列】JavaWeb入门

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

「C++」哈希表的实现(unordered系底层)

&#x1f4bb;文章目录 &#x1f4c4;前言哈希表概念哈希函数 哈希冲突闭散列开散列 &#x1f4d3;总结 &#x1f4c4;前言 unordered系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构&#xff0c;使其在查找上的时间复杂度几乎减低到了 O ( 1 ) O(1) O(1)。 哈希…

企业ERP软件定制开发的重点|app小程序网站建设

企业ERP软件定制开发的重点|app小程序网站建设 随着企业信息化程度的不断提高&#xff0c;企业资源计划&#xff08;ERP&#xff09;软件成为了现代企业管理的重要工具。然而&#xff0c;由于不同企业的业务流程、组织结构和管理模式各异&#xff0c;现有的通用ERP软件无法完全…

基于若依的ruoyi-nbcio的flowable流程管理系统增加服务任务和我的抄送功能

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 1、增加一个状态字段 wf_copy增加下面两个字段 就用未读已读来区分 2、前端 api接口增加如下&#xff…

IDEA检查项目的jdk版本需要看的地方

IDEA检查项目的jdk版本需要看的地方 1、检查项目结构&#xff0c;如下图所示选择即可 选择了之后打开了如下界面&#xff1a; 下面的三张图全部都要检查选择jdk8的版本 2、进入设置&#xff0c;如下所示&#xff1a; 进入之后&#xff0c;根据下图&#xff0c;挨个选择&#xf…

「海蓝色」海关可视化监管平台,助力海关体系实现规范化程序管理

海关监管是国家对进出境货物、旅客和邮件进行检查和控制的重要机构&#xff0c;其职责是保障国家的安全和经济利益。海关监管的核心目标是防止非法进出境活动&#xff0c;包括走私、偷逃税款等行为。海关监管通过检查和核实货物的品质、数量和价值&#xff0c;确保货物符合相关…

SMART PLC编码器角度测量

编码器角度测量在倒立摆摆杆角度测量上的应用请参考下面文章链接: https://rxxw-control.blog.csdn.net/article/details/131443657https://rxxw-control.blog.csdn.net/article/details/131443657线性模式下编码器旋转圈数和单圈计数值测量可以查看下面文章: https://rxxw…

C语言实现植物大战僵尸(完整版)

实现这个游戏需要Easy_X 这个在我前面一篇C之番外篇爱心代码有程序教你怎么下载&#xff0c;大家可自行查看 然后就是需要植物大战僵尸的素材和音乐&#xff0c;需要的可以在评论区 首先是main.cpp //开发日志 //1导入素材 //2实现最开始的游戏场景 //3实现游戏顶部的工具栏…

关于pycharm代码误删,本地历史恢复

关于pycharm代码误删&#xff0c;本地历史恢复 1.工作中或多或少的出现代码误删&#xff0c;或者其他原因导致我们的辛辛苦苦写的码子没了&#xff0c;一定很着急。 2.我们点击工作目录文件夹或者是项目内鼠标右击&#xff0c;找到local history 然后选择show history,就可以…

Python3+RIDE+RobotFramework自动化测试框架搭建过程详解

一、Python安装 最新版Python下载地址&#xff1a;https://www.python.org/ 根据操作系统选择对应版本制品下载安装即可&#xff0c;本机用的是Windows x86-64 executable installer。 注意事项&#xff1a; 安装完成后检查下环境变量&#xff0c;默认会配置好&#xff0c;可…

算法初阶双指针+C语言期末考试之编程题加强训练

双指针 常⻅的双指针有两种形式&#xff0c;⼀种是对撞指针&#xff0c;⼀种是左右指针。 对撞指针&#xff1a;⼀般⽤于顺序结构中&#xff0c;也称左右指针。 • 对撞指针从两端向中间移动。⼀个指针从最左端开始&#xff0c;另⼀个从最右端开始&#xff0c;然后逐渐往中间逼…