《javaEE篇》--阻塞队列详解

阻塞队列

阻塞队列概述

阻塞队列也是一种队列,和普通队列一样遵循先进先出的原则,但是阻塞队列相较于普通队列多了两项功能阻塞添加阻塞移除,使得阻塞队列成为一种线程安全的数据结构

  • 阻塞添加:当队列满的时候继续入队就会阻塞等待,直到有线程从队列中取走一个元素之后,队列才能重新添加新元素
  • 阻塞移除:当队列为空时继续出队此时也会阻塞等待,直到有线程在队列中添加新的元素之后,队列才可以继续删除元素

生产者消费者模型 

生产者消费者模型就是通过一个中间容器,来解决生产者和消费者之间的强耦合作用。

生产者和消费者不直接接触,而是由这个中间容器来完成二者之间的交流,生产者将生产的数据直接扔到中间容器里,消费者直接从中间容器里拿数据。

这里的中间容器就相当于是一个仓库,当生产者生产的数据太多时仓库满了,此时再向仓库内输入数据就会阻塞,相反当仓库为空时,消费者在向仓库索要数据,也会发生阻塞。这样就可以很好的平衡两者之间的处理能力。

生产者的生产速率和消费者的消费速率往往是不同的,难免会出现生产速率暴增或消费效率暴增的情况(比如各大电商平台的双11活动),这时如果让生产者和消费者直接对接很有可能就会出现问题(比如服务器挂掉),所以要使用这样一个中间容器来平衡,生产者可以按照自己的速率向容器中填充数据,消费者也可以按照自己的速率拿取数据

阻塞队列的作用

解耦合

把阻塞队列封装成单独的服务器程序,将服务器a接收到的指令传给阻塞队列,再将指令从阻塞队列传到服务器b,此时,耦合就会被降低,如果b这边出现问题,就不会对a产生直接影响

削峰填谷

举个例子🌰:

假如在某电商平台的双11活动,此时用户发送的请求会突然暴增,如果服务器a收到的每一个请求都发给服务器b,那么,a和b所接触到的访问量就一样了,虽然ab的访问量一样,但是可能由于a和b上所跑的业务可能不一样,就会导致单个访问所消耗的硬件资源不一样,最终造成,A可以承受住这些访问量,但是b不可以。比如b要操作数据库,数据库本身就是分布式系统中相对脆弱的环节。

如果此时使用阻塞队列,A将收到的大量请求先写入到队列中,b就可以按照,之前的节奏来处理请求

  • 削峰就相当于阻塞队列帮b承担了来自a的大量访问的压力,B可以按照之前的速度来进行处理,而且像上述峰值的情况一般,不会持续存在,只会短时间出现。
  • 填谷就相当于,峰值之后a的请求量就恢复正常或者降低了,b就可以逐渐的把积压的数据都给处理掉

标准库中的阻塞队列

在 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可.

  • LinkedBlockingQueue<>    链表实现的阻塞队列
  • LinkedTransferQueue<>    链表实现的无界阻塞队列
  • ArrayBlockingQueue<>      顺序表实现的有界阻塞队列
  • PriorityBlockingQueue<>    支持优先级排序的阻塞队列(由堆实现)
  • synchronousQueue<>    不存储元素的阻塞队列,每次入队操作必须伴随一个出队操作

//比较常用的就是 LinkedBlockingQueue<> 

一般使用的BlockingQueue<>只是一个接口,真正实现的类是LinkedBlockingQueue<> 

阻塞队列常用的阻塞入队操作是put,阻塞出队操作是take

实现阻塞队列

环形队列

博主这里使用数组环形队列来实现一个阻塞队列,环形队列是一种特殊的队列,在逻辑上是环形的数组首尾相连,但是实际上是一个定长的数组,环形队列依然保持队列先入先出的特点。

下面我通过画图来描绘一下

定义一个head表示队头,在定义一个tail表示队尾(实际上是指向队尾的下一个位置),在初始状态下队列为空,head和tail重合

入队列操作就是,先在tail处插入新的元素,然后让tail++。出队列就是,把head位置的数据删除掉,直接让head++就行(逻辑删除),已经删除的数据会被之后插入的数据覆盖掉,当head和tail再一次重和就意味着队列为空了。数组内真正有效的数据就是区间[head,tail)内的数据。

但是这样做还有一个问题,当队列满的时候

此时head和tail就又重合了,队列空的时候他俩重合,队列满的时候他俩也是重合,这样似乎就不太好判断队列究竟是满还是空。这里有两种方法可以解决

  1. 浪费一个格子让tail指向head前一个时,队列就算满了
  2. 专门定义一个变量size来表示队列里的有效元素个数,当size=0时队列为空,size=”数组长度“时为满

两种方法都可以,这里我采用的是第二种方法

代码展示

public class AnnularQueue {private String[] elems =new String[100];//队首private int head = 0;//队尾private int tail = 0;//有效元素个数private int size = 0;//入队方法public void put(String elem) throws InterruptedException {//当队列已满时if(size == elems.length) {return;}//将元素插入队尾elems[tail] = elem;tail++;//超过数组长度后回到数组首位if(tail >= elems.length) {tail = 0;}//有效元素总数加一size++;}//出队方法public String tack() throws InterruptedException {String elem = null;//当队列为空时if(size == 0) {return elem;}//出队(逻辑删除)elem = elems[head];head++;if(head == elems.length) {//回到数组首位head = 0;}//存入后有效元素总数加一size--;//返回取出的元素return elem;}}

实现阻塞队列

之前有提到阻塞队列的特点在于,当队列为满和为空时都会进行阻塞等待,所以我们只需要在刚刚实现的环形队列上加入这个功能就行。

代码展示

class MyBlockingQueue{private String[] data = new String[1000];private volatile int head = 0;private volatile int tail = 0;private volatile int size = 0;public void put(String elem) throws InterruptedException {synchronized (this){//队列是否满了//为了确认wait在唤醒之后,还可以再确认条件是否成立while(size == data.length){//如果是普通队列就直接返回//return;//如果是阻塞队列就直接waitthis.wait();}data[tail] = elem;tail++;size++;//如果tail自增之后到达数组末尾,就让tail重新回到数组开头if(tail == data.length){tail = 0;}//这个notify是用来唤醒因为队列为空而引起的阻塞//take中的waitthis.notify();}}public String take() throws InterruptedException {synchronized (this){//队列是否为空//while(size == 0){//如果是普通队列就直接返回空//return null;//如果是阻塞队列就直接等待this.wait();}//队列不为空,就去返回队首head处数据String elem = data[head];head++;size--;if(head == data.length){head = 0;}//这个notify是用来唤醒,因队列满而阻塞的wait//put中的waitthis.notify();return  elem;}}
}

既然我们要在队列满和空时让队列阻塞,那么就要使用wait来让调用该方法的线程等待阻塞

但是不能就这样一直等待下去,还要使用notify来解除等待,根据最开始的分析,当队列因为出队操作而阻塞时,就要等待其他线程进行入队操作,当队列因为入队操作阻塞时,就要等待其他线程进行出队操作。那么我们在tack和put操作最后各加上一个notify,这样就可以实现上面的功能了。因为如果队列处在阻塞状态下,必然是因为满了或者为空,而且必须是在其他线程完成tack/put操作之后,在可以使用notify解阻塞。

  • put的notify用来唤醒因为tack而引起的阻塞
  • tack的notify用来唤醒因为put而引起的阻塞

 这里还有一个问题wait一定会被notify唤醒吗?或者说只有notify可以唤醒wait吗?

当然不止,还会因为interrupt方法直接中断当前的线程,不过我们刚刚使用了throws InterruptedException,会使线程直接报异常然后结束整个方法,这样是没问题的,但是如果使用try-catch就会处bug了。

此时方法并不会结束,而是会继续往下执行,如果队列已经满了的话,队列的最后一个元素就会被新插入的元素覆盖掉,但是被覆盖的元素并不是一个无效元素,而且此时size也比数组长度大,这样显然是不合理的。

所以在这种情况下我们要注意当前唤醒wait是notify还是interrupt,如果是notify证明此时队列已经不满可以继续插入元素,如果是interrupt唤醒,则此时队列还是瞒着的如果插入元素就会出现问题。

虽然我们刚刚使用的是throws直接抛出异常,这样就算是interrupt唤醒的也没什么大事,但是以防万一,我们还是要处理一下这个问题。

处理的方法也很简单,当wait被唤醒之后再判断一次队列是否为满,要是为满就继续wait,因为wait也可能被interrupt连续唤醒好几次,所以我们直接使用while作为判断语句,直到队列不满时才可以进行新的入队操作

到这里我们简单实现的一个阻塞队列就算完成了,接下来我们使用这个阻塞队列来实现一个简单的生产者消费者模型

实现生产者消费者模型

//实现生产者消费这模式MyBlockingQueue blockingQueue = new MyBlockingQueue();//生产者Thread t1 = new Thread(() -> {int num = 0;while (true){try {//生产数据blockingQueue.put(num + "");//转换为字符System.out.println("生产:" + num);num++;Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//消费者Thread t2 = new Thread(() -> {while (true){try {//消费数据String elem = blockingQueue.take();System.out.println("消费:" + elem);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();

 //可以通过sleep来调整生产速率和消费速率

运行结果

可以看到我刚刚的代码是消费速率远大于生产速率(生产速率是每秒生产1),所以结果会按照生产速率来执行.

接着我们调整速率,这次是生产速率是按照编译器执行的速度,消费速率是每0.5秒消费1

可以看到生产者瞬间生产了1000个数据,但是消费者任然按照自己的速率去执行,这里阻塞队列就起到了一个削峰填谷的作用

以上就是博主对阻塞队列知识的分享,如果有不懂的或者有其他见解的欢迎在下方评论或者私信博主,也希望多多支持博主之后和博客!!🥰🥰​​​​​​​

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

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

相关文章

css大屏设置中间元素四周渐变透明效果

css大屏设置中间元素四周渐变透明效果 四周透明效果&#xff1a; // 设置蒙版上下左右渐变显示mask-image: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 10%, rgba(0, 0, 0, 1) 90%, rgba(0, 0, 0, 0) 100%),linear-gradient(to bottom, rgba(0, 0, 0…

scrapy 爬取旅游景点相关数据(一)

第一节 Scrapy 练习爬取穷游旅游景点 配套视频可以前往B站&#xff1a;https://www.bilibili.com/video/BV1Vx4y147wQ/?vd_source4c338cd1b04806ba681778966b6fbd65 本项目为scrapy 练手项目&#xff0c;爬取的是穷游旅游景点列表数据 0 系统的环境 现在网上可以找到很多scr…

ArcGIS Desktop使用入门(四)——ArcMap软件彻底卸载删除干净

系列文章目录 ArcGIS Desktop使用入门&#xff08;一&#xff09;软件初认识 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——标准工具 ArcGIS Desktop使用入门&#xff08;二&#xff09;常用工具条——编辑器 ArcGIS Desktop使用入门&#xff08;二&#x…

AV1技术学习:Quantization

量化是对变换系数进行&#xff0c;并将量化索引熵编码。AV1的量化参数 QP 的取值范围是0 ~ 255。 一、Quantization Step Size 在给定的 QP 下&#xff0c;DC 系数的量化步长小于 AC 系数的量化步长。DC 系数和 AC 系数从 QP 到量化步长的映射如下图所示。当 QP 为 0 时&…

全开源收银系统源码-支付通道

1.收银系统开发语言 核心开发语言: PHP、HTML5、Dart后台接口: PHP7.3后合管理网站: HTML5vue2.0element-uicssjs线下收银台&#xff08;安卓/PC收银、安卓自助收银&#xff09;: Dart3框架&#xff1a;Flutter 3.19.6助手: uniapp商城: uniapp 2.支付通道 智慧新零售收银系统…

使用vscode,cmake,opencv gpu进行硬解码视频

在此之前需要基于msvc编译器来编译gpu版opencv&#xff0c;详细内容请查看windows下不依赖Visual Studio编译gpu版opencv&#xff0c;支持硬解码硬编码 vscode安装&#xff1a;这个就不赘述了&#xff0c;安装完vscode后需要安装cmake tools插件配置opencv的dll库路径&#xf…

Windows版MySQL8.4.2LTS解压直用(下载免安装-绿色-项目打包直接使用-含卸载)

文章目录 下载地址选择GPL选择server选择8.5.2LTS长期服务版 配置解压配置环境变量初始化新建my.ini配置root密码安装mysql启动mysql服务 使用mysql登陆mysql修改mysql密码设置客户端可连接远程MySQL服务器修改host为值为”%“ 执行语句: update user set host% where userroot…

K210视觉识别模块学习笔记8:Mx_yolo3本地模型训练环境搭建_部署模型到亚博canmv(失败)

今日开始学习K210视觉识别模块: 本地模型训练环境搭建 亚博智能 K210视觉识别模块...... 固件库: canmv_yahboom_v2.1.1.bin 本地训练 Mx_yolo3 这里就简单地提示一下下载安装哪些软件&#xff0c;然后主要是使用Mx_yolo3 进行本地训练模型的...... 本文不…

第13周 简历职位功能开发与Zookeeper实战

第13周 简历职位功能开发与Zookeeper实战 本章概述1. Mysql8窗口函数over使用1.1 演示表结构与数据1.2 案例1:获取男女总分数1.3 案例2****************************************************************************************本章概述 1. Mysql8窗口函数over使用 参考案例…

【算法】分布式共识Paxos

一、引言 在分布式系统中&#xff0c;一致性是至关重要的一个问题。Paxos算法是由莱斯利兰伯特&#xff08;Leslie Lamport&#xff09;在1990年提出的一种解决分布式系统中一致性问题的算法。 二、算法原理 Paxos算法的目标是让一个分布式系统中的多个节点就某个值达成一致。算…

Linux--Socket编程预备

目录 1. 理解源 IP 地址和目的 IP 地址 2.端口号 2.1端口号(port)是传输层协议的内容 2.2端口号范围划分 2.3理解 "端口号" 和 "进程 ID" 2.4理解 socket 3.传输层的典型代表 3.1认识 TCP 协议 3.2认识 UDP 协议 4. 网络字节序 5. socket 编程接…

代码随想录day21 二叉树最后一天 || 669修剪二叉树 108将有序数组转变为平衡搜索二叉树 538把搜索二叉树变为累加二叉树

669修剪二叉树 力扣题目链接 题目描述&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果…

基于Neo4j将知识图谱用于检索增强生成:Knowledge Graphs for RAG

Knowledge Graphs for RAG 本文是学习https://www.deeplearning.ai/short-courses/knowledge-graphs-rag/这门课的学习笔记。 What you’ll learn in this course Knowledge graphs are used in development to structure complex data relationships, drive intelligent sea…

【BUG】已解决:UnicodeDecodeError: ‘utf-8’ codec can’t decode bytes in position 10

UnicodeDecodeError: ‘utf-8’ codec can’t decode bytes in position 10 目录 UnicodeDecodeError: ‘utf-8’ codec can’t decode bytes in position 10 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#x…

SpringBoot3 JDK21 Vue3开源后台RBAC管理系统 | 2024年好用的开源RBAC管理系统 | 数据权限的探索

序言 项目现已全面开源&#xff0c;商业用途完全免费&#xff01; 当前版本&#xff1a;v0.7.2。 如果喜欢这个项目或支持作者&#xff0c;欢迎Star、Fork、Watch 一键三连 &#x1f680;&#xff01;&#xff01; 在构建此代码框架的过程中&#xff0c;我已投入了大量精力&…

Flink内存管理机制

前言 在Flink的后台界面&#xff0c;可以看到整个Flink的内存情况。 如JobManager的内存情况&#xff1a; TaskManager的内存情况 一、Flink内存管理 Flink TaskManager内存组成整体结构图如下&#xff1a; 二、总内存管理 三、JobManager内存管理内存管理 四、TaskManager内…

视频主题Qinmei 3.0视频站源码_WordPress影视视频主题/附详细安装教程

Qinmei 3.0主题主要是将 wordpress 改造成纯 api 的站点&#xff0c;以便实现前后端分离的技术栈&#xff0c;目前的进度已经大致完成&#xff0c;唯一的问题就是需要安装 JWT token 插件。 功能介绍&#xff1a; 支持豆瓣以及 bangumi 的一键获取信息, 豆瓣 api 目前使用的是…

blender顶点乱飞的问题解决

初学blender&#xff0c;编辑模式下移动某些顶点&#xff0c;不管是移动还是滑动都会出现定点乱飞的问题&#xff0c;后来才发现是开了吸附工具的原因&#xff01;&#xff01;&#xff01;&#xff01; 像下面这样&#xff0c;其实我只是在Z轴上移动&#xff0c;但是就跑的很…

02 Golang面向对象编程_20240727 课程笔记

视频课程 最近发现越来越多的公司在用Golang了&#xff0c;所以精心整理了一套视频教程给大家&#xff0c;这个是其中的第二部&#xff0c;后续还会有很多。 视频已经录制完成&#xff0c;完整目录截图如下&#xff1a; 课程目录 01 结构体的声明.mp402 使用var根据结构体…

SQL labs-SQL注入(四,sqlmap对于post传参方式的注入)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 序言&#xff1a;本文主要讲解基于SQL labs靶场&#xff0c;sqlmap工具进行的post传参方式的SQL注入。 传参方式有两类&#xff0c;一类是直接在url栏内进行url编码后进行的传参&am…