【JUC并发编程】ArrayBlockingQueue和LinkedBlockingQueue源码2分钟看完
文章目录
- 1、BlockingQueue
- 1)接口方法
- 2)阻塞队列分类
- 2、ArrayBlockingQueue
- 1)构造函数
- 2)put()入队
- 3)take()出队
- 3、LinkedBlockingQueue
- 1)构造函数
- 2)put()入队
- 3)take()出队
1、BlockingQueue
BlockingQueue是JUC包下提供的一个阻塞队列 接口;
1)接口方法
队列操作
- 抛出异常:add(e)、remove()、element()
- 返回特定值:offer()队尾入队/poll()删除队头元素/peek()
- 一直阻塞:put(e)/take()
- 超时退出:offer(e,time,unit)/poll(time,unit)
其中:BlockingQueue 不接受 null 元素。试图 add 、 put 或 offer ⼀个 null 元素时,某些实现会抛出 NullPointerException 。
2)阻塞队列分类
-
ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
-
LinkedBlockingQueue:由链表组成的有界阻塞队列。
-
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列(默认升序排序)。
-
DelayQueue:支持延时获取元素的无界阻塞队列。
队列使用PriorityQueue来实现。
-
SynchronousQueue:一个不存储元素的阻塞队列。
-
LinkedTransferQueue:由链表组成的无界阻塞队列。
其多了tryTransfer()方法和transfer()方法。
- transfer():可以把生产者传入的元素立刻传输给消费者。如果没有consumer在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。
- tryTransfer():用来试探生产者传入的元素是否能直接传给消费者。不管是否有consumer正在等待接收元素,都立刻返回。
2、ArrayBlockingQueue
ArrayBlockingQueue是由数组结构组成的有界阻塞队列。通过ReentrantLock保证线程安全、并实现 Producer-Consumer模式。
1)构造函数
从构造函数可知,默认采用非公平锁;
public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();// 底层使用对象数组保存元素this.items = new Object[capacity];// 初始化需要加锁使用的ReentrantLock实例,默认采用非公平锁lock = new ReentrantLock(fair);/*** 判断队列是 空 or 满* notEmpty⽤于执⾏take时进⾏await()等待操作,put时进⾏signal()唤醒操作* notFull⽤于执⾏take时进⾏signal()唤醒操作,put时进⾏await()等待操作*/notEmpty = lock.newCondition();notFull = lock.newCondition();
}
方法释义:
- 构造函数的入参capacity指定了底层存储元素数组⻓度的⼤⼩;
- 初始化需要加锁使⽤的ReentrantLock实例,默认采⽤的是⾮公平锁;
- 基于Lock的Condition判断队列是 空 or 满
- notEmpty⽤于执⾏take时进⾏await()等待操作,put时进⾏signal()唤醒操作;
- notFull⽤于执⾏take时进⾏signal()唤醒操作,put时进⾏await()等待操作;
2)put()入队
public void put(E e) throws InterruptedException {// 入参不能为空checkNotNull(e);final ReentrantLock lock = this.lock;// 加可中断锁lock.lockInterruptibly();try {while (count == items.length)// 队列满了,则阻塞等待signal唤醒,同时释放次有的锁。notFull.await();// 入队操作enqueue(e);} finally {lock.unlock();}
}private void enqueue(E x) {final Object[] items = this.items;items[putIndex] = x;// 如果数据已经插入到数组末尾,则重置putIndex为0,从0开始继续插入。if (++putIndex == items.length)putIndex = 0;count++;// 通知take线程解除阻塞notEmpty.signal();
}
方法释义:
- ⾸先尝试获得可中断锁,即:lock.lockInterruptibly(),当执⾏interrupt操作时,该锁可以被中断。
- 如果数组中元素的个数(count)等于数组的⻓度了,说明队列已经满了,在该线程上执⾏等待操作:
notFull.await();
,等待signal唤醒。- 如果队列没有满,则调⽤enqueue(e)⽅法执⾏⼊列操作;
- ⼊列操作⾸先会将待插⼊值x放⼊数组下标为putIndex的位置上,然后再将putIndex加1,来指向下⼀次插⼊的下标位置。
- 如果加1后的putIndex等于了数组的⻓度,那么说明已经越界了(因为putIndex是从0开始的);则
做循环式插⼊
,重置putIndex为0,从0开始继续插入。- 最后,执⾏count++来计算元素总个数;并调⽤notEmpty.signal()⽅法来解除阻塞;
- 当队列为空的时候,执⾏take⽅法会被notEmpty.await()阻塞;
- 此处就是通知take线程解除阻塞。
3)take()出队
public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0)// 如果队列为空,则线程阻塞 等待signal唤醒,释放持有的锁notEmpty.await();// 执行出队操作return dequeue();} finally {lock.unlock();}
}private E dequeue() {final Object[] items = this.items;@SuppressWarnings("unchecked")// 获取takeIndex下标的元素E x = (E) items[takeIndex];// 将takeIndex下标下的袁术置为null,便于后面GC回收items[takeIndex] = null;// 如果出队操作的到了数组末尾,则重置takeIndex,从0开始继续取出if (++takeIndex == items.length)takeIndex = 0;count--;// 默认itrs为null,不会走进if代码段。if (itrs != null)itrs.elementDequeued();// 通知put线程解除阻塞notFull.signal();return x;
}
方法释义:
take⽅法和put⽅法类似,区别是出队的指针是takeIndex;
⾸先尝试获得可中断锁,即:lock.lockInterruptibly(),当执⾏interrupt操作时,该锁可以被中断。
如果队列中为空;
执⾏notEmpty.await()
将线程阻塞 等待signal唤醒,释放持有的锁。
- 当调⽤put⽅法向队列中放⼊元素之后 ,会调⽤notEmpty.signal⽅法对等待的线程执⾏唤醒操作;
如果出队操作的到了数组末尾,则重置takeIndex,从0开始继续取出;
出队执⾏完毕后,调⽤
notFull.signal
⽅法来唤醒在notFull上⾯await的线程。 通知put线程解除阻塞。
3、LinkedBlockingQueue
LinkedBlockingQueue是由链表结构组成的有界阻塞队列。通过ReentrantLock保证线程安全、并实现 Producer-Consumer模式。
1)构造函数
如果不指定容量,则默认LinkedBlockingQueue是无界阻塞队列(capacity = Integer.MAX_VALUE)
构造函数中会创建⼀个空的节点,作为整个链表的头节点。
2)put()入队
private void enqueue(Node<E> node) {// assert putLock.isHeldByCurrentThread();// assert last.next == null;last = last.next = node;
}
整个put()流程和ArrayBlockingQueue基本一致,对于链表容量的统计会额外采用一个AtomiceInteger类型的变量count维护。
-
- 最后唤醒put()线程的代码段上有一个
c == 0
的判断,这里的c是入队操作之前的数量。
- 最后唤醒put()线程的代码段上有一个
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
这里有个比较有意思的点:
- 入队操作添加完元素之后,如果发现当前队列的元素数量还没到最大容量,则尝试唤醒其他put()操作阻塞的线程;
if (c + 1 < capacity)notFull.signal();
3)take()出队
private E dequeue() {// assert takeLock.isHeldByCurrentThread();// assert head.item == null;Node<E> h = head;Node<E> first = h.next;h.next = h; // help GChead = first;E x = first.item;first.item = null;return x;
}
整个take()流程和ArrayBlockingQueue基本一致,稍微看一下即可。
- 最后唤醒take()线程的代码段上有一个
c == capacity
的判断,这里的c是出队操作之前的数量。
和LinkedBlockingQueue的put()操作一样:
- 出队操作移除完元素之后,如果发现当前队列的元素数量 > 1,则尝试唤醒其他take()操作阻塞的线程;
if (c > 1)notEmpty.signal();
相关文章:

【JUC并发编程】ArrayBlockingQueue和LinkedBlockingQueue源码2分钟看完
文章目录1、BlockingQueue1)接口方法2)阻塞队列分类2、ArrayBlockingQueue1)构造函数2)put()入队3)take()出队3、LinkedBlockingQueue1)构造函数2)put()入队3)take()出队1、Blocking…...

GitHub个人资料自述与管理主题设置
目录 关于您的个人资料自述文件 先决条件 添加个人资料自述文件 删除个人资料自述文件 管理主题设置 补充:建立一个空白文件夹 关于您的个人资料自述文件 可以通过创建个人资料 README,在 GitHub.com 上与社区分享有关你自己的信息。 GitHub 在个…...
Express篇-连接mysql
创建数据库配置文件config/sqlconfig.jsconst sqlconfig {host: localhost, // 连接地址user: root, //用户名password: ****, //密码port: 3306 , //端口号database: mysql01_dbbooks //数据库名 } module.exports sqlconfig封装数据库管理工具 utils/mysqlUtils.…...

win10 安装rabbitMQ详细步骤
win10 安装rabbitMQ详细步骤 win10 安装rabbitMQ详细步骤win10 安装rabbitMQ详细步骤一、下载安装程序二、安装配置erlang三、安装rabbitMQ四、验证初始可以通过用户名:guest 密码guest来登录。报错:安装RabbitMQ出现Plugin configuration unchanged.问题…...

【成为架构师课程系列】一线架构师:6个经典困惑及其解法
目录 一线架构师:6个经典困惑及其解法 多阶段还是多视图? 内置最佳实践 架构方法论:3个阶段,一个贯穿 Pre-architecture阶段:ADMEMS矩阵方法 Conceptual Architecture阶段:重大需求塑造做概念架构 Refined Architecture…...

光耦合器的定义与概述
光耦合器或光电耦合器是一种电子元件,基本上充当具有不同电压电平的两个独立电路之间的接口。光耦合器是可在输入和输出源之间提供电气隔离的常用元件。它是一个 6 引脚器件,可以有任意数量的光电探测器。 在这里,光源发出的光束作为输入和输…...

谷粒商城--品牌管理详情
目录 1.简单上传测试 2.Aliyun Spring Boot OSS 3.模块mall-third-service 4.前端 5.数据校验 6.JSR303数据校验 7.分组校验功能 8.自定义校验功能 9.完善代码 1.简单上传测试 OSS是对象存储服务,有什么用呢?把图片存储到云服务器上能让所有人…...

stack、queue和priority_queue
目录 一、栈(stack) 1.stack的使用 2.容器适配器 3.stack的模拟实现 二、队列(queue) 1.queue的使用 2.queue的模拟实现 三、双端队列(deque) 1.vector,list的优缺点 2.认识deque 四…...

面试题(二十二)消息队列与搜索引擎
2. 消息队列 2.1 MQ有什么用? 参考答案 消息队列有很多使用场景,比较常见的有3个:解耦、异步、削峰。 解耦:传统的软件开发模式,各个模块之间相互调用,数据共享,每个模块都要时刻关注其他模…...

Spring Security in Action 第三章 SpringSecurity管理用户
本专栏将从基础开始,循序渐进,以实战为线索,逐步深入SpringSecurity相关知识相关知识,打造完整的SpringSecurity学习步骤,提升工程化编码能力和思维能力,写出高质量代码。希望大家都能够从中有所收获&#…...

Java面试——maven篇
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

基于微信小程序的游戏账号交易小程序
文末联系获取源码 开发语言:Java 框架:ssm JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏览器…...

Matlab绘制隐函数总结-二维和三维
1.二维隐函数 二维隐函数满足f(x,y)0f(x,y)0f(x,y)0,这里无法得到yf(x)yf(x)yf(x)的形式。不能通过普通函数绘制。 我们要关注的是使用fplot函数和fimplicit函数。 第1种情况:基本隐函数 基本的隐函数形式形如: x2y22x2(x2y2)12x^{2}y^{…...
如何直观地理解傅立叶变换?频域和时域的理解
如何直观地理解傅立叶变换 傅里叶变换连续形式的傅立叶变换如何直观地理解傅立叶变换?一、傅里叶级数1.1傅里叶级数的三角形式1.2 傅里叶级数的复指数形式二、傅里叶变换2.1一维连续傅里叶变换三、频谱和功率谱3.1频谱的获得3.2频谱图的特征3.3频谱图的组成频域(frequency do…...

STC15读取内部ID示例程序
STC15读取内部ID示例程序🎉本案例基于STC15F2K60S2为验证对象。 📑STC15 ID序列介绍 STC15系列STC最新一代STC15系列单片机出厂时都具有全球唯一身份证号码(ID号)。最新STC15系列单片机的程序存储器的最后7个字节单元的值是全球唯一ID号,用…...

Xml格式化与高亮显示
具体请参考:Xml格式化与高亮显示...

【GlobalMapper精品教程】045:空间分析工具(2)——相交
GlobalMapper提供的空间分析(操作)的方法有:交集、并集、单并集、差异、对称差集、相交、重叠、接触、包含、等于、内部、分离等,本文主要讲述相交工具的使用。 文章目录 一、实验数据二、符号化设置三、相交运算四、结果展示五、心灵感悟一、实验数据 加载配套实验数据(…...

4年外包终上岸,我只能说这类公司能不去就不去..
我大学学的是计算机专业,毕业的时候,对于找工作比较迷茫,也不知道当时怎么想的,一头就扎进了一家外包公司,一干就是4年。现在终于跳槽到了互联网公司了,我想说的是,但凡有点机会,千万…...

sklearn降维算法1 - 降维思想与PCA实现
目录1、概述1.1 维度概念2、PCA与SVD2.1 降维实现2.2 重要参数n_components2.2.1 案例:高维数据的可视化2.2.2 最大似然估计自选超参数2.2.3 按信息量占比选超参数1、概述 1.1 维度概念 shape返回的结果,几维几个方括号嵌套 特征矩阵特指二维的 一般来…...

「期末复习」线性代数
第一章 行列式 行列式是一个数,是一个结果三阶行列式的计算:主对角线的乘积全排列与对换逆序数为奇就为奇排列,逆序数为偶就为偶排列对换:定理一:一个排列的任意两个元素对换,排列改变奇偶性(和…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...

【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...