阻塞队列BlockingQueue解析

article/2025/8/11 15:27:54

阻塞队列是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除的方法。

阻塞插入:当队列满的时候,队列会阻塞插入元素的线程,直到队列不满。

阻塞移除:当队列空的时候,队列会阻塞移除元素的线程,直到队列不满。

对于插入和移除的操作则是提供了四种处理方式

抛出异常:当队列满时候插入元素的时候则会直接抛出异常,为空时取出元素也会抛出元素。

返回特殊值:这种方式不会阻塞线程而是直接返回boolean类型的值表示是否插入成功或者移除成功

一直阻塞:插入或者移除的操作不满足条件的时候会进行阻塞放入Condition的队列中直到满足条件后被唤醒

超时退出:进行阻塞是含有时间限制的,超出时间之后线程则会退出方法,返回boolean类型的值

如果采用的是无界队列那么进行使用put或者offer方法的时候永远不会阻塞,并且使用offer方法时候永远返回true

JDK 7 提供了 7 个阻塞队列,如下。

  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
  • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
  • SynchronousQueue:一个不存储元素的阻塞队列。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

ArrayBlockingQueue是一个由数组实现的有界阻塞队列,此队列按照先进先出的原则对元素进行排序。默认情况下不保证线程公平的访问队列,所谓公平访问队列是指阻塞的线程考研找阻塞的先后顺序访问队列,即先阻塞线程先访问队列。非公平则是当队列可用的时候所有的阻塞线程都会抢夺访问队列的资格。一般公平性通常会降低吞吐量。

   public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();this.items = new Object[capacity];lock = new ReentrantLock(fair);notEmpty = lock.newCondition();notFull =  lock.newCondition();}

初始化ArrayBlockingQueue的时候第一个参数是容量第二个参数则是是否公平访问。

LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序

PriorityBlockingQueue则是一个支持优先级的无界阻塞队列。默认情况下元素采取自然顺序升序排序,也可以指定Comparator参数来实现对元素的排序,或者重写类对象的compareTo方法来进行排序。需要注意的是不能保证同优先级元素的顺序。

DelayQueue是一个支持延迟获取元素的无界阻塞队列。队列使用的是PriorityQueue来实现的队列中的元素必须实现了Delayed接口,在创建元素的时候可以指定多久才能从队列中获取当前的元素。只有延迟期满之后才能从队列中提取元素。DelayQueue使用的场景:缓存设置,使用一个线程循环查询DelayQueue如果获取到元素则表明这个元素的缓存有效期到期了,定时任务调度,当获取到元素的时候表示当前元素已经经历了指定时间的等待。

SynchronousQueue则是一个不存储元素的阻塞队列,队列中的每个put操作必须等待一个take操作才会执行否则不会执行,支持公平访问队列,默认情况下为非公平策略的访问。也就是说SynchronousQueue实际上没有队列而是直接将生产者的产品直接传递给消费者。

LinkedTransferQueue是一个无界的由链表组成的阻塞队列,相比于其他阻塞队列多了一个transfer和tryTransfer的方法来执行,这个方法的特点和SynchronousQueue类似而是将生产者或者消费者的数据直接传递给对方而不存储到队列中去,同时如果没有对应的线程接收数据就会进入等待。而tryTransfer则是不会等待直接返回boolean的结果

源码原理分析:

下面我们就根据源码来进行原理分析:

先从两个核心方法put和take开始逐步解析

    public void put(E var1) throws InterruptedException {checkNotNull(var1);ReentrantLock var2 = this.lock;var2.lockInterruptibly();try {while(this.count == this.items.length) {this.notFull.await();}this.enqueue(var1);} finally {var2.unlock();}}public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0)notEmpty.await();return dequeue();} finally {lock.unlock();}}

在讲述put方法之前我们首先来看看ArrayBlockingQueue的内部属性

    final Object[] items;int takeIndex;int putIndex;int count;final ReentrantLock lock;private final Condition notEmpty;private final Condition notFull;

其中items这个属性则是用来存储元素的,takeIndex:消费者指针,标记队头位置(下一个取出的元素)。putIndex:生产者指针,标记队尾位置(下一个插入的位置),count则是用来表示整个队列的资源数界限的。lock的目的很明显就是为了保证多线程调用时候的同步并且后续的两个Condition队列也是依赖lock来进行的为其提供了保障,同时也能证明阻塞队列是一个线程安全的队列。两个Condtion队列分别表示当前队列为空的时和队列满时候的等待队列。

然后我们开始分析put方法首先是调用了checkNotNull来检查传递的资源是否为空,为空则直接抛出异常。随后取出lock锁设置同步保障,然后通过while循环进行判断当前队列是否已经满了,如果满了就直接调用等待队列notFull来进行线程的阻塞处理(当线程被唤醒之后则会继续执行后续的方法),为什么设置while循环呢?确保条件真正满足:避免虚假唤醒和竞争条件导致的状态不一致。随后就调用enqueue来讲元素进行入队设置,更改索引以及队列数组等属性

最后我们再看看take方法,几乎与put方法差不多都是先获取锁然后判断当前队列条件是否满足不满足则会调用等待队列来讲线程进行阻塞。区别在于一个是将元素放到队列中去,另一个则是将元素进行取出。这就是阻塞队列的两个核心方法。

总结一下:

维度实现机制解决的问题
线程安全ReentrantLock + 临界区操作防止多线程数据竞争
阻塞控制Condition.await()/signal()精准线程挂起/唤醒
空间效率循环指针(takeIndex/putIndex)O(1)复杂度避免数据迁移
状态管理count 原子计数器快速判断空/满状态
资源协调生产者-消费者条件分离(notFull/notEmpty)解耦生产消费速率差异

阻塞队列的本质其实是依赖于Condition的同步队列工具来实现对线程的阻塞的,而Condtion又依赖于ReentrantLock的锁机制来实现线程的入队出队,这两个同步工具的底层依赖则都是AQS。AQS提供了这些操作的底层实现逻辑并且搭配LockSupport内的Unsafe本地方法来实现线程控制的精准控制,这些底层工具类一层层的堆叠从而完成了阻塞队列的阻塞功能。


http://www.hkcw.cn/article/TUICqkdCHT.shtml

相关文章

[Redis] Redis命令在Pycharm中的使用

初次学习&#xff0c;如有错误还请指正 目录 String命令 Hash命令 List命令 set命令 SortedSet命令 连接pycharm的过程见&#xff1a;[Redis] 在Linux中安装Redis并连接桌面客户端或Pycharm-CSDN博客 redis命令的使用见&#xff1a;[Redis] Redis命令&#xff08;1&#xf…

车载控制器的“机电一体化”深度集成

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所谓鸡汤&#xff0c;要么蛊惑你认命&#xff0c;要么怂恿你拼命&#xff0c;但都是回避问题的根源&…

PINN模型相关原理

PINN模型相关原理 目录 PINN模型相关原理原本的物理界的利用神经网络的参数估计PINN 的原理介绍一、基本思想二、PINN 的损失函数三、自动微分&#xff08;Autodiff&#xff09;四、PINN 的优势与挑战 原本的物理界的利用神经网络的参数估计 原本物理界需要确定一个三维流体&a…

计算机基础——宏病毒防御与网络技术

文章目录 宏病毒详解与防范措施宏病毒简介宏病毒的特点宏病毒的传播途径宏病毒的防范措施宏病毒的检测与清除 自治计算机与自治系统解析什么是自治计算机&#xff1f;技术特点 自治系统&#xff08;Autonomous System, AS&#xff09;特点&#xff1a;自治系统类型 总结&#x…

MySql(十一)

目录 准备工作 1&#xff09;准备一张表 2&#xff09;插入数据 分组 1&#xff09;通过性别去统计各组的平局工资 2.limit关键字 不使用limit的关键字 使用limit的关键字 使用limit关键字获取从指定行开始获取 准备工作 1&#xff09;准备一张表 CREATE table role(roleid INT…

论文阅读(六)Open Set Video HOI detection from Action-centric Chain-of-Look Prompting

论文来源&#xff1a;ICCV&#xff08;2023&#xff09; 项目地址&#xff1a;https://github.com/southnx/ACoLP 1.研究背景与问题 开放集场景下的泛化性&#xff1a;传统 HOI 检测假设训练集包含所有测试类别&#xff0c;但现实中存在大量未见过的 HOI 类别&#xff08;如…

使用 SASS 与 CSS Grid 实现鼠标悬停动态布局变换效果

最终效果概述 页面为 3x3 的彩色格子网格&#xff1b;当鼠标悬停任意格子&#xff0c;所在的行和列被放大&#xff1b;使用纯 CSS 实现&#xff0c;无需 JavaScript&#xff1b;利用 SASS 的模块能力大幅减少冗余代码。 HTML 结构 我们使用非常基础的结构&#xff0c;9 个 .i…

linux 后记

Linux Server 下载一个Server的版本&#xff0c;就是那种只有命令行的 学会这个就可以去租一个aliyun服务器&#xff0c;挺便宜的 如果在aliyun买服务器的话就不用管镜像源 但是如果是自己的虚拟机就必须设置镜像源&#xff0c;上网搜索阿里的镜像源&#xff0c;然后手动输入&…

2025年第三届CCF·夜莺开源创新论坛通知

点击蓝字 关注我们 CCF Opensource Development Committee 01 大会简介 由中国计算机学会主办、CCF开源发展委员会及夜莺开源社区承办的第三届CCF夜莺开源创新论坛拟于2025年7月4日在北京召开。本次论坛以“AI 加速可观测”为主题&#xff0c;汇聚了开源夜莺核心开发团队&#…

【2025CCF中国开源大会】RISC-V 开源生态的挑战与机遇分论坛重磅来袭!共探开源芯片未来

点击蓝字 关注我们 CCF Opensource Development Committee 开源浪潮正从软件席卷硬件领域&#xff0c;RISC-V作为全球瞩目的开源芯片架构&#xff0c;正在重塑计算生态的版图&#xff01;相较于成熟的x86与ARM&#xff0c;RISC-V生态虽处爆发初期&#xff0c;却蕴藏着无限可能。…

分布式流处理与消息传递——Kafka ISR(In-Sync Replicas)算法深度解析

Java Kafka ISR&#xff08;In-Sync Replicas&#xff09;算法深度解析 一、ISR核心原理 #mermaid-svg-OQtnaUGNQ9PMgbW0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-OQtnaUGNQ9PMgbW0 .error-icon{fill:#55222…

力扣题解106:从中序与后序遍历序列构造二叉树

一、题目内容 题目要求根据二叉树的中序遍历序列和后序遍历序列来重建二叉树。具体来说&#xff0c;我们需要利用中序遍历序列和后序遍历序列的特点&#xff0c;通过递归的方法逐步构建出完整的二叉树。 中序遍历序列的特点是&#xff1a;左子树 -> 根节点 -> 右子树。后…

基于微信小程序的scratch学习系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

win11回收站中出现:查看回收站中是否有以下项: WPS云盘回收站

好久没更新了&#xff0c;首先祝所有大朋友、小朋友六一儿童节快乐&#xff0c;真的希望我们永远都不会长大呀&#xff0c;长大真的好累呀(•_•) 免责声明 笔者先来个免责声明吧&#xff0c;被网上的阴暗面吓到了 若读者参照笔者的这篇文章所执行的操作中途或后续出现的任何…

基于springboot的运动员健康管理系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

6、修改和校正时间

一、输入date命令可以看到系统的日期时间 date (后面的CST表示中国标准时间) 二、如果显示时间比当前时间慢了8小时&#xff0c;那就要设置一下时区 sudo dpkg-reconfigure tzdata 选择Asia 选择Shanghai 三、树莓派没有电池&#xff0c;断电后无法保存时间。树莓派默认安…

MySQL基础查询

目录 一、表中的增删查改 1.1直接插入 1.2更新 1.3替换 二、Retrieve 2.1Select列 2.1.1where子句 2.1.2结果排序 三、Update 四、Delete 五、截断表 六、插入查询结果 6.1案例&#xff1a;对表中数据去重 七、聚合函数 八、分组统计group by子句 一、表中的增删查改 创建creat…

怎么样提高研发质量?

提高研发质量是提升项目成功率、降低风险和增强客户满意度的关键。常见的有效的方法和策略&#xff0c;可以帮助提高研发质量&#xff1a; 一、建立明确的质量目标和标准 制定质量目标 &#xff1a;在项目启动阶段&#xff0c;明确质量目标&#xff0c;确保团队成员对质量期望…

MCU如何从向量表到中断服务

目录 1、中断向量表 2、编写中断服务例程 中断处理的核心是中断向量表&#xff08;IVT&#xff09;&#xff0c;它是一个存储中断服务例程&#xff08;ISR&#xff09;地址的内存结构。当中断发生时&#xff0c;MCU通过IVT找到对应的ISR地址并跳转执行。本文将深入探讨MCU&am…

Docker Compose(容器编排)

目录 什么是 Docker Compose Docker Compose 的功能 Docker Compose 使用场景 Docker Compose 文件&#xff08;docker-compose.yml&#xff09; Docker Compose 命令清单 常见命令说明 操作案例 总结 什么是 Docker Compose docker-compose 是 Docker 官方的开源项…