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

阻塞队列:线程安全与生产者消费者模型解析

一、阻塞队列

阻塞队列就是基于普通队列做出扩展

1.线程安全的

如果针对一个已经满了的队列进行入队列,此时入队列操作就会阻塞,一直阻塞到队列不满(其他线程出队列元素)之后

如果针对一个已经空了的队列进行出队列,此时出队列操作就会阻塞,一直阻塞到队列不空(其他线程入队列元素)之后

包饺子

A和B和C在过年期间要包饺子,为了快一点三个人决定一起包饺子

但包饺子得先擀饺子皮,现在就只有一个擀面杖,所以A擀饺子皮是BC就陷入了阻塞,显然这就会出现竞争擀面杖问题(锁竞争)。

我们加入了一个桌子(阻塞队列),A负责擀饺子皮,BC通过桌子来包饺子,

如果A干的特别快,BC赶不上他的速度,很快桌子就会被放满(队列满了),这下A就会停下来(阻塞)

如果BC干的特别快,A赶不上他俩的速度,桌子为空(队列为空),BC就得等待(阻塞)

 生产者消费者模型

1.引入生产者消费者模型,就可以更好的做到“解耦合”

     上述过程中,A和B,A和C的耦合性较强,如果B或者C挂了,对于A的影响是很大的

加了阻塞队列A和B,C几乎几乎就没有交集了,B,C就算是挂了,对于A的影响也是微乎其微

阻塞队列(消息队列)

1.上述的阻塞队列,是基于这个数据结构实现的服务器程序,又被部署到单独的主机上
2.整个系统的结构更复杂了,你要维护的服务器更多了

3.效率,请求从A发过来B受到,这个过程经过转发要有一定开销

2.削峰填谷

当外网的请求数量就像洪水一样,急剧增加,这时A的请求数量就会增加很多,即使工作一般比较简单,每个请求消耗的资源少,但架不住多。B和C完成的工作更复杂,消耗的资源更多,这就可能导致BC挂了。

我们加入了阻塞队列之后

队列就像抗洪用的水库,只是用来存储数据的,消耗资源很少,抗压能力很强,在数据多的时候,存下来保持稳定的速度,少的是否就会放出数据,同样保持稳定请求速度,需要注意的是这个队列只是防止BC挂掉了。

Java标准库提供了现成的阻塞队列数据结构

 使用put 和 offer一样都是入队列

但是put是带有阻塞功能,offer 没带阻塞(队列满了会返回结果)

take 方法用来出队列,同样带有阻塞功能

阻塞队列没有提供带有阻塞功能的获取队首元素的方法     

尝试实现一个阻塞队列

既然涉及到了出队列,那数据结构就得用到循环队列,保证空间利用率

class MyBlockingQueue{private String[] elems = null;private int head = 0;private int tail = 0;private int size = 0;//准备锁对象,使用this也可以private Object locker = new Object();public MyBlockingQueue(int capacity){elems= new String[capacity];}public void put(String elem) throws InterruptedException {while(size >= elems.length){}//新的元素要放到tail指向的位置上elems[tail] = elem;tail++;if(tail >= elems.length){tail = 0;}size++;}public String take() throws InterruptedException {String elem = null;while(size == 0){}//取出 head 位置的元素返回elem = elems[head];head++;if(head >= elems.length){head = 0;}size--;return elem;}}
现在我们就相当于实现了一个循环队列,我们需要加上阻塞效果,我们知道只有当队列满和队列空才会阻塞,而解除阻塞则需要队列中有一个元素出去和队列中有一个元素进来。

所以我们可以在put结束时加上notify,并在判断队列满了的时候进行阻塞,至于在判断条件加上while循环,是为了防止在多线程情况下被不小心解锁,毕竟,每次put,take之后都对解一次锁,为了保证代码的原子性,我们为整个方法都加上锁。试想一下如果没有这么加锁,就会引发数组越界等一系列问题。

 take方法也是同理

至此我们可以来验证一下阻塞队列是否成功,我们创建了两个线程分别是t1,t2,t1线程为生产元素的线程并且要比t2线程消费元素快的多。

Thread t1 = new Thread(() -> {int n = 1;while(true){try {queue.put(n + "");System.out.println("生产元素" + n);n++;} catch (InterruptedException e) {throw new RuntimeException(e);}}});//消费者Thread t2 = new Thread(() -> {while (true){try {String n = queue.take();System.out.println("消费元素" + n);Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t2.start();t1.start();

 

我们发现在一开始运行时,t1线程十分快都到了一千多了,但t2线程才到2,这就导致了t1线程要想继续添加元素,就得等t2线程,这就出现了消费一个元素,就生产一个元素。

 定时器

public class ThreadDemo30 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {//时间到了之后要执行的代码System.out.println("hello timer 3000");}},3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello timer 2000");}}, 2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello timer 1000");}},1000);System.out.println("hello main");}
}

我们发现进程并没有结束,Timer 里内置了前台线程

实现一个简单timer

 要想实现定时器,得先剖析它,怎么剖析呢,我们发现定时器就像一个容器一样将线程存储起来,等线程的时间到了就调度,时间短的限制性,这个容器我们就可以用

优先级队列

而优先级队列存储的是啥呢?优先级队列比较器基准是啥呢?

 存储的这个元素得有时间和进程,这时没问题的,我们主要的是根据时间来做文章,首先便是初始化问题,时间得是相对时间所以我们发现

在构造方法上我们是现在的时间加上等待的时间、

其次就是比较器

我们比较的基准是时间,时间少的往前走反之往后走,

比较的顺序往往是试出来的,而不是去猜出来的

回到优先级队列那里,我们要有一个方法创建任务对象,并且将任务放入队列中 

接下来是进行任务的时刻

我们将代码放到构造方法中

我们得有一个线程来做遍历队列中任务的工作

我们之所以加上了锁

是因为防止线程安全问题出现,试着想一下没有锁会咋样,可能队列中都没有元素了它还进行访问,可能队列中的这个元素都出去来,还去执行

为了保持原子性还是得加上锁的,在漫长的等待后,队列进入元素,locker重新获得锁,需要注意的是我们每次去往队列添加一个元素都会解锁。之后我们得获取当前时间和任务规定的时间进行计较,如果没达到约定时间就要进行等待。

至此我们就完成了定时器的设计了,我们发现做完后好像云里雾里啊,我们来重新画图梳理

 首先

因为定时是时间越短越先执行,所以我们用优先级队列来存储线程

我们存储的元素不仅要有线程还要有时间,并且还要有比较器

我们元素构建好了得把元素放到队列中就衍生出了schdule方法,它负责创建对象,并将对象放入队列

接下来我们就要执行进程,我们选在了构造方法里

一进来就先加锁,防止出现线程安全问题,然后判断是否为空,如果为空就要等待,等schdule方法放入对象,并notify。

然后比较当前时间和任务预期执行时间,没到预期时间就去等待相应时间、

到达时间出队列,并且运行线程。

结束!!!

相关文章:

阻塞队列:线程安全与生产者消费者模型解析

一、阻塞队列 阻塞队列就是基于普通队列做出扩展 1.线程安全的 如果针对一个已经满了的队列进行入队列,此时入队列操作就会阻塞,一直阻塞到队列不满(其他线程出队列元素)之后 如果针对一个已经空了的队列进行出队列&#xff0c…...

【入门|Docker】基础知识扫盲:什么是 Docker?

文章目录 基础知识扫盲:什么是 Docker?Docker 是什么?Docker 核心组件Docker 与虚拟机的区别Docker 在现代开发中的核心角色Docker 的局限性 基础知识扫盲:什么是 Docker? 最近打算开始系统性地学习与云计算相关的技术…...

如何利用 Java 爬虫获得某书笔记详情:实战指南

在知识分享和学习的领域,许多平台提供了丰富的书籍笔记和学习资源。通过 Java 爬虫技术,我们可以高效地获取这些笔记的详细信息,以便进行进一步的分析和整理。本文将详细介绍如何利用 Java 爬虫获取某书笔记详情,并提供完整的代码…...

【MYSQL】基本查询,表的增删查改

📚 博主的专栏 🐧 Linux | 🖥️ C | 📊 数据结构 | 💡C 算法 | 🅒 C 语言 | 🌐 计算机网络 |🗃️ mysql 摘要:本文详细介绍了MySQL中的CRUD操作(创…...

在嵌入式系统中, 一般链路层断开多久,断开TCP为好

一、典型场景与推荐策略 1. 实时性优先(工业控制、自动化设备) 需求:快速释放资源,避免因等待重传浪费内存或阻塞任务。 策略: 立即断开:在lwip_netif_link_callback中检测到链路断开后直接关闭TCP连接&a…...

Android Studio 日志系统详解

文章目录 一、Android 日志系统基础1. Log 类2. 日志级别 二、Android Studio 中的 Logcat1. 打开 Logcat2. Logcat 界面组成3. 常用 Logcat 命令 三、高级日志技巧1. 自定义日志工具类2. 打印方法调用栈3. 打印长日志4. JSON 和 XML 格式化输出 四、Logcat 高级功能1. 自定义日…...

基于matlab的D2D 功率控制仿真

基于MATLAB的D2D(Device-to-Device)功率控制仿真示例,包含系统建模、功率控制算法实现和性能分析。该仿真以蜂窝网络为背景,重点关注D2D用户间的干扰管理和功率优化。 1. 系统模型与参数设置​ clc; clear; close all;%% 参数配置…...

互联网大厂Java面试:从基础到复杂场景的技术挑战

互联网大厂Java面试:从基础到复杂场景的技术挑战 场景描述 在一家知名互联网大厂的会议室里,面试官严肃地坐在桌子的一侧,而对面则是一位充满喜感的应聘者——谢飞机。面试官准备了一系列关于Java技术栈的提问,涵盖了从基础到复…...

使用Redission来实现布隆过滤器

简述布隆过滤器 布隆过滤器是一种概率型数据结构,它可以用来判断一个元素是否在一个集合中。我们当时使用的是Redisson实现的布隆过滤器。它的底层原理是,先初始化一个比较大的数组,里面存放的是二进制0或1。一开始都是0,当一个k…...

为 Windows 和 Ubuntu 中设定代理服务器的详细方法

有时下载大模型总是下载不出来,要配置代理才行 一、Windows代理设置 ① 系统全局代理设置 打开【设置】→【网络和Internet】→【代理】。 在【手动设置代理】下,打开开关,输入: 地址:10.10.10.215 端口:…...

Feign异步模式丢失上下文问题

Feign异步模式丢失上下文问题 问题描述 当我们使用异步对我们代码进行操作优化时,代码中使用了RequestContextHolder去获取上下文的数据,当我们执行原来可以执行的业务时发现报了空指针异常或数据为空,这是为什么呢? 原理解释 …...

OpenCV阈值处理完全指南:从基础到高级应用

引言 阈值处理是图像处理中最基础、最常用的技术之一,它能够将灰度图像转换为二值图像,为后续的图像分析和处理奠定基础。本文将全面介绍OpenCV中的各种阈值处理方法,包括原理讲解、代码实现和实际应用场景。 一、什么是阈值处理&#xff1…...

【AWS入门】Amazon SageMaker简介

【AWS入门】Amazon SageMaker简介 [AWS Essentials] Brief Introduction to Amazon SageMaker By JacksonML 机器学习(Machine Learning,简称ML) 是当代流行的计算机科学分支技术。通常,人们在本地部署搭建环境,以满足机器学习的要求。 AWS…...

ArcGIS Pro 3.4 二次开发 - 内容

环境:ArcGIS Pro SDK 3.4 .NET 8 文章目录 内容1 工程1.1 创建一个空工程1.2 使用指定名称创建新工程1.3 使用Pro的默认设置创建新工程1.4 使用自定义模板文件创建新工程1.5 使用 ArcGIS Pro 提供的模板创建工程1.6 打开现有工程1.7 获取当前工程1.8 获取当前工程的…...

如何在 MongoDB 中设计文档结构?与关系型数据库的表结构设计有何不同?

在 MongoDB 中设计文档结构是一个核心且重要的环节,它直接影响应用的性能、可扩展性和可维护性。 MongoDB 文档结构设计原则与方法 MongoDB 的核心思想是数据如何被应用访问,就如何存储它。 嵌入 (Embedding / Denormalization) vs. 引用 (Referencing…...

MYSQL 故障排查与生产环境优化

目录 一.前置知识点 1. 案例需求 (1)mysql 常见故障解决 (2)mysql 性能优化 2.案例实施思路 (1)单库常见故障分析 (2)主从常见故障分析 (3)从几个不同…...

解决使用@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss“, timezone = “GMT+8“)时区转换无效的问题

前言 对于一些时间的字段,我们从数据库查询出来通常需要转换后返回给前端展示,前端需要的格式一般为yyyy-MM-dd HH:mm:ss,可以通过JsonFormat注解来作转换和时区转换。 问题场景 原因 LocalDateTime类本身不带时区信息所以转换无效 解决办…...

计算机网络概要

⽹络相关基础知识 协议 两设备之间使⽤光电信号传输信息数据 要想传递不同信息 那么⼆者ᳵ就需要约定好的数据格式 层 封装 继承 多态是计算机的性质 它们⽀持了软硬件分层的实现 同层协议可以ᳵ接通信 同层协议ᳵ不直接通信 是各⾃调⽤下层提供的结构能⼒完成通信 分层…...

Word压缩解决方案

Word压缩解决方案:基于图片压缩的 .docx 优化实践 📌 背景 在日常科研写作或项目文档整理中,Word 文档(.docx)往往因为插入大量高清图表、扫描图像、公式图等导致文件体积过大,或者毕业学位论文查重要求上…...

Spring Boot开发—— 整合Lucene构建轻量级毫秒级响应的全文检索引擎

文章目录 一、为什么选择 Lucene?轻量级搜索的底层密码二、核心原理:Lucene 的倒排索引2.1 倒排索引:速度之源2.2 段合并优化策略三、Spring Boot集成Lucene实战3.1 依赖配置3.2 实体与索引设计3.3 核心索引服务(含异常处理)3.4 使用示例(测试类)四、高级优化技巧4.1 索…...

TDengine 2025年产品路线图

TDengine OSS 之 2025 年年度路线图如下表所示。 季度功能2025Q1 虚拟表查询能力:REGEXP、GREATEST、LEAST、CAST 函数支持判断表达式、单行选择函数的其他列值、INTERP 支持插值时间范围存储能力:支持将查询结果写入超级表、超级表支持 KEEP 参数、STM…...

vue3中element-plus修改el-tooltip的宽度

注意选中的样式是 :deep(.el-popper .is-dark){max-width:30% !important; } 也有可能不需要后面的 .is-dark,看情况而定,总之不是.el-tooltip 修改el-tooltip的显示高度参考:前端字数太多使用el-table设置show-overflow-tooltip显示不下&a…...

Ubuntu服务器部署多语言项目(Node.js/Python)方式实践

Ubuntu服务器部署多语言项目(Node.js/Python)方式实践 服务器脚本运行方式命令行直接执行nohup后台执行进程 Screen概述安装基本操作命令启动 Screen退出当前会话(不终止进程)查看所有会话重连会话关闭会话 常用快捷键典型使用场景…...

计算机网络 - 2.基础协议

1.TCP协议 1.TCP(Transmission Control Protocol):传输控制协议2.TCP协议是一种面向连接的、可靠的、 基于字节流的传输层通信协议 1.面向连接:两个使用TCP协议的应用(通常一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接2.可靠的 1.数据传输之前都要建立…...

Kafka消息路由分区机制深度解析:架构设计与实现原理

一、消息路由系统的核心架构哲学 1.1 分布式系统的三元悖论 在分布式消息系统的设计过程中,架构师需要平衡三个核心诉求:数据一致性、系统可用性和分区容忍性。Kafka的分区路由机制本质上是对CAP定理的实践解: 一致性维度:通过…...

机器学习中采样哪些事

在机器学习中采样主要分为两种,过采样(Oversample)和欠采样(Undersample)。过采样就是通过增加少数类样本的数量来平衡数据集。而欠采样就是通过减少多数类样本的数量来平衡数据集。 通常在进行采样中以下是几种常用的方法: 1. 随机采样 随…...

初识css,css语法怎样学好css以及常见问题与避坑

一、CSS 是什么? CSS(Cascading Style Sheets)是一种用于描述网页文档(HTML 或 XML)呈现样式的语言。它负责控制网页元素的视觉表现,如颜色、字体、布局等,使内容与展示分离。 二、CSS 语法结构…...

MySQL如何快速删除数据库中所有表数据

首先运行下面指令生成TRUNCATE TABLE语句 -- 生成的TRUNCATE TABLE语句 SELECT CONCAT(TRUNCATE TABLE , table_name, ;) FROM information_schema.tables WHERE table_schema axe_elder; 再运行下面指令禁用外键关联检查 -- 禁用外键检查 SET FOREIGN_KEY_CHECKS 0; 运…...

计算机视觉与深度学习 | Python实现ARIMA-LSTM时间序列预测(完整源码和数据)

ARIMA-LSTM混合模型 1. 环境准备2. 数据生成(示例数据)3. 数据预处理4. ARIMA建模5. LSTM残差建模6. 混合预测7. 结果可视化完整代码说明1. **数据生成**2. **ARIMA建模**3. **LSTM残差建模**4. **混合预测**5. **性能评估**参数调优建议扩展方向典型输出以下是使用Python实现…...

Axure疑难杂症:垂直菜单展开与收回(4大核心问题与专家级解决方案)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢!如有帮助请订阅专栏! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:垂直菜单展开与收回 主要内容:超长菜单实现、展开与收回bug解释、Axure9版本限制等问题解…...