JavaEE多线程案例之阻塞队列
上文我们了解了多线程案例中的单例模式,此文我们来探讨多线程案例之阻塞队列吧
1. 阻塞队列是什么?
阻塞队列是⼀种特殊的队列.也遵守"先进先出"的原则.
阻塞队列是⼀种线程安全的数据结构,并且具有以下特性:
- 当队列满的时候,继续⼊队列就会阻塞,直到有其他线程从队列中取⾛元素.
- 当队列空的时候,继续出队列也会阻塞,直到有其他线程往队列中插⼊元素.
阻塞队列的⼀个典型应用场景就是"生产者消费者模型".这是⼀种非常典型的开发模型
那么什么是生产者消费者模型呢??
1.1 生产者消费者模型
1. 生产者消费者模式就是通过⼀个容器来解决生产者和消费者的强耦合问题
生产者和消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找⽣产者要数据,而是直接从阻塞队列⾥取.
应用场景:
一对夫妻开包子店,早上夫妻二人包包子,女的负责擀包子皮(擀好的包子皮放在桌板上(类似于阻塞队列)),男的负责包包子,其中擀包子皮的就是生产者,包包子的就是消费者。擀包子皮的人不在意谁消耗他生产出来的包子皮,包包子的人也不在意是谁生产的包子皮,能用就可以
2. 阻塞队列就相当于⼀个缓冲区,平衡了⽣产者和消费者的处理能⼒. (削峰填⾕)
应用场景:
在每年的双十一、双十二购物节,服务器同⼀时刻可能会收到大量的支付请求,如果同时处理这些支付请求服务器可能扛不住(每个支付请求的处理都需要比较复杂的流程).
这个时候就可以把这些请求都放到⼀个阻塞队列中(类似于上面提到的放包子皮的桌板,消费者不需要按照生产者的请求速度来完成,可以按照自己的速度来完成,就保证了不会服务器崩掉),
然后再由消费者线程慢慢的来处理每个⽀付请求.这样做可以有效进⾏ “削峰”, 防⽌服务器被突然到来的⼀波请求直接冲垮.
3. 阻塞队列可以实现异步操作
1.2 标准库中的阻塞队列
在 Java 标准库中内置了阻塞队列. 如果我们需要在⼀些程序中使⽤阻塞队列, 直接使⽤标准库中的即可.
BlockingQueue
是⼀个接口. 真正实现的类是LinkedBlockingQueue
.- put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.
- BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.
1.3 消息队列的自我实现
- 首先我们采用的是循环队列的方式实现消息队列
定义一个数组用于存放消息
定义一个头尾指针标记消息队列的队头和队尾
定义一个size用于标记有效数据个数
//定义一个消息队列数组
private Integer [] information=null;
//定义一个头指针
private int head=0;
//尾指针
private int tail=0;
//定义一个size用于标记有效数据个数
private int size=0;
- 此时我们数组不给定大小,我们采用构造方法在初始化时,给定数组大小,此时和JDK实现的雷同
//构造方法用于初始化消息队列的容量大小
public MyBlockQueue(Integer capacity) {if(capacity<=0){throw new RuntimeException("队列容量必须大于0.");}information=new Integer[capacity];
}
- 在队尾插入元素
为了实现消息队列的效果,就要与普通队列不一样 在插入元素时,
若消息队列满了,就等待,等到消息队列有空位时,就会被唤醒
当插入元素时,就唤醒出元素的的操作
其中,wait()方法、notifyAll()需要搭配synchronize关键字使用
public void put(int value) throws InterruptedException {//没有位置就要等待if(size>=information.length){synchronized (this){this.wait();}}//有位置直接插入元素,无需等待if(size<information.length){information[tail]=value;tail++;size++;if(tail>=information.length){tail=0;}synchronized (this){this.notifyAll();}}}
- 在队头出元素
为了实现消息队列的效果,当消息队列中没有元素时,就需要等待,直到有元素进来,再被唤醒
当出元素时,有位置空余出来,就可以唤醒要插入的操作
//取出元素public int take() throws InterruptedException {//队列为空,需要等待if(size==0){synchronized (this){this.wait();}}//有元素直接取出即可Integer value=information[head];head++;size--;if(head==information.length){head=0;}synchronized (this){this.notifyAll();}return value;}
- synchronize关键字实现了原子性、在效果上实现了内存可见性,但是没有保证有序性,所以我们
为涉及修改的变量加上volatile关键字
//定义一个消息队列数组private volatile Integer [] information=null;//定义一个头指针private volatile int head=0;//尾指针private volatile int tail=0;//定义一个size用于标记有效数据个数private volatile int size=0;
- 由于在插入和删除的操作中涉及多个变量的修改,我们可以扩大synchronize的范围
此时,我们完整的代码如下:
public class MyBlockQueue {//定义一个消息队列数组private volatile Integer [] information=null;//定义一个头指针private volatile int head=0;//尾指针private volatile int tail=0;//定义一个size用于标记有效数据个数private volatile int size=0;//构造方法用于初始化消息队列的容量大小public MyBlockQueue(Integer capacity) {if(capacity<=0){throw new RuntimeException("队列容量必须大于0.");}information=new Integer[capacity];}//插入元素public void put(int value) throws InterruptedException {synchronized (this){//没有位置就要等待if(size>=information.length){this.wait();}//有位置直接插入元素,无需等待if(size<information.length){information[tail]=value;tail++;size++;if(tail>=information.length){tail=0;}synchronized (this){this.notifyAll();}}}}//取出元素public int take() throws InterruptedException {synchronized (this){//队列为空,需要等待if(size==0){synchronized (this){this.wait();}}//有元素直接取出即可Integer value=information[head];head++;size--;if(head==information.length){head=0;}this.notifyAll();return value;}}
}
- 此时引发一个新问题,假设此时有多个线程要进行put操作,但只有一个空余位置,会有什么问题吗
所以我们将if判断改成while判断,并且在JDK提供的put方法中也是用while
1.4 消息队列代码
public class MyBlockQueue {//定义一个消息队列数组private volatile Integer [] information=null;//定义一个头指针private volatile int head=0;//尾指针private volatile int tail=0;//定义一个size用于标记有效数据个数private volatile int size=0;//构造方法用于初始化消息队列的容量大小public MyBlockQueue(Integer capacity) {if(capacity<=0){throw new RuntimeException("队列容量必须大于0.");}information=new Integer[capacity];}//插入元素public void put(int value) throws InterruptedException {synchronized (this){//此处最好使⽤ while.//否则notifyAll 的时候, 该线程从wait 中被唤醒, 但是紧接着并未抢占到锁.//当锁被抢占的时候, 可能⼜已经队列满了while (size>=information.length){this.wait();}information[tail]=value;tail++;size++;if (tail>information.length){tail=0;}this.notifyAll();}}//取出元素public int take() throws InterruptedException {synchronized (this){//队列为空,需要等待while (size==0){this.wait();}//有元素直接取出即可Integer value=information[head];head++;size--;if(head==information.length){head=0;}this.notifyAll();return value;}}
}
希望得到你的支持,谢谢!
相关文章:

JavaEE多线程案例之阻塞队列
上文我们了解了多线程案例中的单例模式,此文我们来探讨多线程案例之阻塞队列吧 1. 阻塞队列是什么? 阻塞队列是⼀种特殊的队列.也遵守"先进先出"的原则. 阻塞队列是⼀种线程安全的数据结构,并且具有以下特性: 当队列满的时候,继续⼊队列就会…...

梳理你的思路(从OOP到架构设计)_基本OOP知识04
目录 1、 主动型 vs.基於被动型 API 1)卡榫函数实现API 2)API的分类 3)回顾历史 4)API >控制力 2、 结语&复习: 接口与类 1)接口的表示 2)Java的接口表示 1、 主动型 vs.基於被动…...

nginx反向代理(负载均衡)
nginx的代理 代理 四层代理 七层代理 正向代理和缓存的配置方式 🐭🐮🐯🐰🐉🐍🐴🐑🐒🐔🐶🐷 反向代理》负载均衡 负载均衡ÿ…...
Android系统应用主要模块
设置 Android Settings开发总结 Launcher Android Launcher开发学习总结 System UI Android SystemUI 学习总结...

【万字详解】三维重建(二)——NeRF、NeuS、MeshUDF、NeuralUDF、3DGS、GShell
文章目录 一、NeRF:Representing Scenes as Neural Radiance Fields for View Synthesis(推荐读)1.1 式1 神经网络的输入和输出1.2 式2 体素渲染算法1.3 式3 损失函数1.4 位置编码1.5 基本原理二、经典的重建流程2.1 传统的三维重建pipeline2.2 神经网络回归2.3 可微渲染最优…...
【RK3588 Linux 5.x 内核编程】-内核线程与Seqlock
内核线程与Seqlock 文章目录 内核线程与Seqlock1、Seqlock介绍2、Seqlock相关API2.1 初始化2.2 写操作2.3 读操作3、驱动实现4、驱动验证在前面的文章中,我们介绍了 Mutex、Spinlock、Read/Write Spinlock 的使用及其实现。 它们都用于保护共享资源不被两个或多个进程同时修改…...

访问者模式的理解和实践
在软件开发过程中,设计模式为我们提供了解决常见问题的最佳实践。访问者模式(Visitor Pattern)是行为设计模式之一,它将数据操作与数据结构分离,使得在不修改数据结构的前提下,能够定义作用于这些元素的新的…...
在Scala中对Map函数的使用
package pp28{object cscc {def main(args: Array[String]): Unit {val m1 Map("马云 — 阿里巴巴" -> 1964,"马化腾 — 腾讯" -> 1971,"李彦宏 - 百度" -> 1968,"雷军 - 小米" -> 1969,"丁磊 - 网易" -> …...
PyTorch基本使用-张量的索引操作
在操作张量时,经常要去获取某些元素进行处理或者修改操作,在这里需要了解torch中的索引操作。 准备数据: data torch.randint(0,10,[4,5]) print(data--->,data)输出结果: data---> tensor([[3, 9, 4, 0, 5],[7, 5, 9, …...

OpenCV实验:图片加水印
第二篇:图片添加水印(加 logo) 1. 实验原理 水印原理: 图片添加水印是图像叠加的一种应用,分为透明水印和不透明水印。水印的实现通常依赖于像素值操作,将水印图片融合到目标图片中,常用的方法…...
sql server log文件
确定 SQL Server 实例中具有大量 VDF 的数据库 SELECT [name], COUNT(l.database_id) AS vlf_count FROM sys.databases AS s CROSS APPLY sys.dm_db_log_info(s.database_id) AS l GROUP BY [name] HAVING COUNT(l.database_id) > 100; 在收缩日志文件之前确定事务日志中…...

Elasticsearch 集群部署
Elasticsearch 是一个分布式的搜索和分析引擎,广泛应用于日志分析、全文搜索、实时数据分析等场景。它以其高性能、高可用性和易用性而著称。本文档将引导您完成一个基本的 Elasticsearch 集群配置,包括节点间的通信、客户端访问、安全设置等关键步骤。我…...

微信小程序5-图片实现点击动作和动态加载同类数据
搜索 微信小程序 “动物觅踪” 观看效果 感谢阅读,初学小白,有错指正。 一、功能描述 a. 原本想通过按钮加载背景图片,来实现一个可以点击的搜索button,但是遇到两个难点,一是按钮大小调整不方便(网上搜索…...
策略梯度定理公式的详细推导
策略梯度定理公式的详细推导 以下是策略梯度定理公式从基础概率公式到最终形式的完整推导,帮助更清晰地理解推导过程中的每一个步骤。 1. 策略梯度的目标 我们希望最大化期望累积奖励 ( J ( θ ) J(\theta) J(θ) ),其定义为: J ( θ ) E…...

力扣-图论-10【算法学习day.60】
前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程(例如想要掌握基础用法,该刷哪些题?)我的解析也不会做的非常详细,只会提供思路和一些关键点,力扣上的大佬们的题解质量是非…...
《Python WEB安全 库全解析》
《Python WEB安全 库全解析》 一、Python WEB安全 库概述二、常见的 Python WEB安全 库介绍1. Jiasule2. Awesome Python Security3. Flask-Security4. Flask-SeaSurf 三、Python WEB 安全库的优缺点1. 优点2. 缺点 四、Python WEB 安全库的使用场景1. 开发 Web 应用2. 处理敏感…...
Linux yum-config-manager命令异常
错误信息 使用 yum-config-manager命令时错误信息如下 sudo yum-config-manager \ > --add-repo \ > https://download.docker.com/linux/centos/docker-ce.repo sudo: yum-config-manager: command not found 解决办法 第一步: sudo yum -y install yum-u…...
ios 开发配置蓝牙
如果使用了蓝牙功能, 又没有配置, 会出现以下错误: This app has crashed because it attempted to access privacy-sensitive data without a usage description. The apps Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaini…...

geoserver(1) 发布sql 图层 支持自定义参数
前提使用postgis 数据库支持关联 join 支持 in,not in,like,及其他sql原生函数 新增sql图层 编写自定义sql 编辑sql语句必须输出带有geom数据 正则表达式去除 设置id以及坐标参考系 预览sql图层效果 拼接sql参数 http://xxx.com/geoserver/weather/wms?SERVICEWMS&VERSI…...
Linux:network:添加ip的时候自动添加一个本地路由
文章目录 问题问题 最近在看一个路由的问题,顺便看内核代码,发现在添加IP的时候,内核会自动添加一个local route。 net/ipv4/devinet.c inet_rtm_newaddr->__inet_insert_ifa /* Send message first, then call notifier.Notifier will trigger FIB update, so thatlis…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...