AQS底层源码深度剖析-BlockingQueue
目录
AQS底层源码深度剖析-BlockingQueue
BlockingQueue定义
队列类型
队列数据结构
ArrayBlockingQueue
LinkedBlockingQueue
DelayQueue
BlockingQueue API
添加元素
检索(取出)元素
BlockingQueue应用队列总览图
AQS底层源码深度剖析-BlockingQueue【重点中的重点】
看源码前,先要明白Condition的含义:
BlockingQueue源码会涉及三个队列【重点掌握】:
put()源码剖析:
take()源码剖析:
总结:
AQS底层源码深度剖析-BlockingQueue
BlockingQueue定义
线程通信一个工具,在任意时刻,不管并发有多高,在单台JVM上,同一时间永远只能有一个线程能够对队列进行入队或者出队操作。
官方点说:BlockingQueue,是java.util.concurrent包提供的用于解决并发生产者-消费者问题的最有用的类,它的特性是在任意时刻只有一个线程可以进行take或put操作,并且BlockingQueue提供了超时return null的机制,在许多生产应用场景里都可以看到这个工具的身影。
应用场景:
线程池,springcloud-Eureka的三级缓存,Nacos,Netty,MQ
队列类型
- 无限队列 (unbounded queue ) - 几乎可以无限增长
- 有限队列 ( bounded queue ) - 定义了最大容量
队列数据结构
队列实质就是一种存储数据的结构
- 通常用链表或者数组实现
- 一般而言队列具备FIFO先进先出的特性,当然也有双端队列(Deque)优先级队列
- 主要操作:入队(EnQueue)与出队(Dequeue)
常见的4种阻塞队列
- ArrayBlockingQueue 由数组支持的有界队列
- LinkedBlockingQueue 由链接节点支持的可选有界队列
- PriorityBlockingQueue 由优先级堆支持的无界优先级队列
- DelayQueue 由优先级堆支持的、基于时间的调度队列
ArrayBlockingQueue
队列基于数组实现,容量大小在创建ArrayBlockingQueue对象时已定义好
数据结构如下图:
队列创建:
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>();
应用场景
在线程池中有比较多的应用,生产者消费者场景
工作原理
基于ReentrantLock保证线程安全,根据Condition实现队列满时的阻塞
LinkedBlockingQueue
是一个基于链表的无界队列(理论上有界)
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
上面这段代码中,blockingQueue的容量将设置为Integer.MAX_VALUE
向无限容量的队列中添加元素的所有操作都将永远不会阻塞,因此它可以增长到非常大的容量(注意 这里不是说不会加锁保证线程安全,同样会加锁来保证同一时刻只会有一个线程对队列添加元素或取出元素成功)
使用无限容量的BlockingQueue设计生产者-消费者模型时最重要的是消费者应该能够像生产者向队列添加消息一样快的消费消息。否则存储消息数据的内存可能会填满,然后得到一个OutOfMemory异常
DelayQueue
由优先级堆支持的、基于时间的调度队列,内部基于无界队列PriorityQueue实现,而无界队列基于数组的扩容实现。
队列创建:
BlockingQueue<String> blockingQueue = new DelayQueue();
要求:
入队的对象必须要实现Delayed接口,而Delayed集成自Comparable接口
应用场景:
电影票
工作原理:
队列内部会根据时间优先级进行排序。延迟类线程池周期执行。
BlockingQueue API
BlockingQueue 接口的所有方法可以分为两大类:负责向队列添加元素的方法和检索(取出)这些元素的方法。在队列满/空的情况下,来自这两个组的每个方法的行为都不同。
添加元素
方法 | 说明 |
add() | 如果插入成功则返回 true,否则抛出 IllegalStateException 异常 |
put() | 将指定的元素插入队列,如果队列满了,那么会阻塞直到有空间插入 |
offer() | 如果插入成功则返回 true,否则返回 false |
offer(E e, long timeout, TimeUnit unit) | 尝试将元素插入队列,如果队列已满,那么会阻塞直到有空间插入 |
检索(取出)元素
方法 | 说明 |
take() | 获取队列的头部元素并将其删除,如果队列为空,则阻塞并等待元素变为可用 |
poll(long timeout, TimeUnit unit) | 检索并删除队列的头部,如有必要,等待指定的等待时间以使元素可用,如果超时,则返回 null |
在构建生产者 - 消费者程序时,这些方法是 BlockingQueue 接口中最重要的构建块。
BlockingQueue应用队列总览图
应用队列:存储消息数据的队列。无论是consumer还是producer,想要对应用队列中的消息数据进行操作(存入或取出)时,必须先获取到锁对象。如果获取不到锁,则无法操作。
(1)
极端情况下:
当producer把队列容量放满了,那么producer释放锁,producer阻塞,让consumer获取到锁 然后去消费消息数据
同理当consumer消费完队列中的消息数据,那么consumer会释放锁,consumer阻塞,让producer获取到锁,然后去生产并且加入消息数据到队列中
普通情况下:
当然应用队列中没有放满,consumer也可以消费取出数据 。队列数据没有被消费完时,producer也可以生产消息并且存入应用队列中。
(2) 当producer在同步队列中存入一个消息数据后,会进行通知consumer,consumer接收到通知,会从条件队列中转移到阻塞CLH队列,在CLH阻塞队列中的consumer会进行消费应用队列中存储的消息数据
AQS底层源码深度剖析-BlockingQueue【重点中的重点】
以下会深度剖析BlockingQueue的put()和take()方法的底层实现源码,一步步走完后会进行总结。如果不看源码,就没有任何的说服性。
看源码前,先要明白Condition的含义:
Condition的实现ConditionObject:
ConditionObject是AQS类的内部类,在BlockingQueue底层的实现中主要功能有:等待队列,等待和通知。
等待队列,等待和通知:源码中会使用到notEmpty和notFull
notEmpty: 消费者对应的等待队列。有啥用?当应用队列中的数据被消费完毕后,最后一次消费数据的消费者会释放掉自己持有的锁,然后调用notEmpty.await()加入到notEmpty等待队列的尾部。并且会调用notFull.signal()表示唤醒生产者等待队列中的一个节点加入到CLH阻塞队列中去[因为队列中没有数据啦,所以要唤醒生产者加入到CLH阻塞队列中]。
notFull:生产者对应的等待队列。有啥用?当应用队列中的空间已经被数据占满后,最后一次生产数据的生产者会释放掉自己持有的锁,然后调用notFull.await()加入到notFull等待队列的尾部。并且会调用notEmpty.signal()表示唤醒消费者等待队列中的一个节点加入到CLH阻塞队列中去[因为队列的空间被数据占满啦,所以要唤醒消费者加入到CLH阻塞队列中]。
补充:
其实唤醒也不是说只有当极端情况下(队列被占满或队列为空)才会进行唤醒消费者或生产者,其实每一次往队列中进行加入或取出数据都会导致各自的唤醒操作。
eg:加入一条数据会导致唤醒一个消费者等待队列中的消费者加入到CLH队列。取出一条数据时同理即可。
BlockingQueue源码会涉及三个队列【重点掌握】:
1.应用队列:
存放消息数据,模拟出的一个虚拟队列概念,可以认为是一个虚拟的不存在的存储结构,底层是使用数组进行存储插入应用队列的数据,以此模拟出一个应用队列。
2.CLH双端阻塞队列:存储封装当前线程对象对应的Node节点,是底层真实存在的一个队列
3.条件等待队列:这个就是上面Condition中记录的:notEmpty和notFull
put()源码剖析:
API层面调用put()方法其实就是生产者加入一条数据到应用队列
1.
2.
3.put方法解析
生产者插入一个数据到应用队列,以下为具体的分析过程:
对put()方法中的await()方法解析一下:
对put方法中的equeue()方法解析:
take()源码剖析:
API层面调用take()方法其实就是生产者从应用队列中取出一条数据
1.
2.
3.
对take()中的await方法解析:
对take()中的dequeue方法解析:
总结:
(1)记清楚三个队列
(2)明白Condition的含义
(3)画出图即可
其实也没啥,看懂了也挺简单的,关于这个源码的图,使用processon绘制:
ProcessOn Flowchart
相关文章:

AQS底层源码深度剖析-BlockingQueue
目录 AQS底层源码深度剖析-BlockingQueue BlockingQueue定义 队列类型 队列数据结构 ArrayBlockingQueue LinkedBlockingQueue DelayQueue BlockingQueue API 添加元素 检索(取出)元素 BlockingQueue应用队列总览图 AQS底层源码深度剖析-BlockingQueue【重点中的重…...
Kotlin协程:Flow的异常处理
示例代码如下:launch(Dispatchers.Main) {// 第一部分flow {emit(1)throw NullPointerException("e")}.catch {Log.d("liduo", "onCreate1: $it")}.collect {Log.d("liudo", "onCreate2: $it")}// 第二部分flow …...

qt下ffmpeg录制mp4经验分享,支持音视频(h264、h265,AAC,G711 aLaw, G711muLaw)
前言 MP4,是最常见的国际通用格式,在常见的播放软件中都可以使用和播放,磁盘空间占地小,画质一般清晰,它本身是支持h264、AAC的编码格式,对于其他编码的话,需要进行额外处理。本文提供了ffmpeg录…...
C#读取Excel解析入门-1仅围绕三个主要的为阵地,进行重点解析,就是最理性的应对上法所在
业务中也是同样的功能点实现。只是多扩展了很多代码,构成了项目的其他部分,枝干所在。但是有用的枝干,仅仅不超过三个主要的!所以您仅仅围绕三个主要的为阵地,进行重点解析,就是最理性的应对上法所在了 str…...
一起Talk Android吧(第五百一十八回:在Android中使用MQTT通信五)
文章目录 知识回顾问题描述解决过程经验分享各位看官们大家好,这一回中咱们说的例子是" 在Android中使用MQTT通信五",本章回内容与前后章节内容无关联。闲话休提,言归正转,让我们一起Talk Android吧! 知识回顾 我们在前面章回中介绍了如何使用MQTT通信,包含它…...

100种思维模型之混沌与秩序思维模型-027
人类崇尚秩序与连续性,我们习惯于我们的日常世界,它以线性方式运作,没有不连续或突跳。 为此,我们学会了期望各种过程以连续方式运行,我们的内心为了让我们更有安全感,把很多事物的结果归于秩序,…...

Java开发 - Redis初体验
前言 es我们已经在前文中有所了解,和es有相似功能的是Redis,他们都不是纯粹的数据库。两者使用场景也是存在一定的差异的,本文目的并不重点说明他们之间的差异,但会简要说明,重点还是在对Redis的了解和学习上。学完本…...
Python - 使用 pymysql 操作 MySQL 详解
目录创建连接 pymsql.connect() 方法的可传参数连接对象 conn pymsql.connect() 方法游标对象 cursor() 方法使用示例创建数据库表插入数据操作数据查询操作数据更新操作数据删除操作SQL中使用变量封装使用简单使用: import pymysqldb pymysql.connect(host,user…...

机器学习-卷积神经网络CNN中的单通道和多通道图片差异
背景 最近在使用CNN的场景中,既有单通道的图片输入需求,也有多通道的图片输入需求,因此又整理回顾了一下单通道或者多通道卷积的差别,这里记录一下探索过程。 结论 直接给出结论,单通道图片和多通道图片在经历了第一…...

考研复试——计算机组成原理
文章目录计算机组成原理1. 计算机系统由哪两部分组成?计算机系统性能取决于什么?2. 冯诺依曼机的主要特点?3. 主存储器由什么组成,各部分有什么作用?4. 什么是存储单元、存储字、存储字长、存储体?5. 计算机…...

硬件设计 之摄像头分类(IR摄像头、mono摄像头、RGB摄像头、RGB-D摄像头、鱼眼摄像头)
总结一下在机器人上常用的几种摄像头,最近在组装机器人时,傻傻分不清摄像头的种类。由于本人知识有限,以下资料都是在网上搜索而来,按照摄像头的分类整理一下,供大家参考: 1.IR摄像头: IRinfr…...
PTA:C课程设计(2)
山东大学(威海)2022级大一下C习题集(2)2-5-1 字符定位函数(程序填空题)2-5-2 判断回文(程序填空题)2-6-1 数字金字塔(函数)2-6-2 使用函数求最大公约数(函数)2-6-3 使用函数求余弦函…...

第四章:面向对象编程
第四章:面向对象编程 4.1:面向过程与面向对象 面向过程(POP)与面向对象(OOP) 二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象&…...

Linux 安装npm yarn pnpm 命令
下载安装包 node 下载地址解压压缩包 tar -Jxf node-v19.7.0-linux-x64.tar.xz -C /root/app echo "export PATH$PATH:/app/node-v16.9.0-linux-x64" >> /etc/profile source /etc/profile ln -sf /app/node-v16.9.0-linux-x64/bin/npm /usr/local/bin/ ln -…...

linux SPI驱动代码追踪
一、Linux SPI 框架概述 linux系统下的spi驱动程序从逻辑上可以分为3个部分: SPI Core:SPI Core 是 Linux 内核用来维护和管理 spi 的核心部分,SPI Core 提供操作接口,允许一个 spi master,spi driver 和 spi device 在 SPI Cor…...
Ls-dyna材料的相关学习笔记
Elastic Linear elastic materials -Isotropic:各向同性材料 -orthotropic 正交各向异性的 -anistropic 各向异性的...
Arrays方法(copyOfRange,fill)
Arrays方法 1、Arrays.copyOfRange Arrays.copyOfRange的使用方法 功能: 将数组拷贝至另外一个数组 参数: original:第一个参数为要拷贝的数组对象 from:第二个参数为拷贝的开始位置(包含) to:…...
AcWing - 蓝桥杯集训每日一题(DAY 1——DAY 5)
文章目录一、AcWing 3956. 截断数组(中等)1. 实现思路2. 实现代码二、AcWing 3729. 改变数组元素(中等)1. 实现思路2. 实现代码三、AcWing 1460. 我在哪?(简单)1. 实现思路2. 实现代码四、AcWin…...
RHCSA-文件的其他命令(3.7)
目录 文件的其他命令: 文本内容统计wc 移动和复制(cp) 移动 查找文件的路径 压缩和解压缩 .tar(归档命令) shell-命令解释器 linux中的特殊字符 查看系统上的别名:alias 历史命令(his…...
多线程update导致的mysql死锁问题处理方法
最近想起之前处理过的一个mysql 死锁问题,是在高并发下update批量更新导致的,这里探讨一下发生的原因,以及解决办法; 发生死锁的sql语句如下,其中where条件后的字段是有复合索引的。 update t_push_message_device_h…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...