深入LinkedBlockingQueue实现原理
学习BlockingQueue之LinkedBlockingQueue实现原理
一:概念
LinkedBlockingQueue是一个用链表实现的有界阻塞队列。此队列的默认和最大长度为 Integer.MAX_VALUE。此队列按照先进先出的原则对元素进行排序。
与ArrayBlockingQueue的异同:
ArrayBlockingQueue: 必须设置长度容量 底层数组结构 单锁控制
LinkedBlockingQueue:默认Integer最大值 底层链表结构 双锁
二:LinkedBlockingQueue源码实现
不设置容量,默认为Integer的最大值

也支持设置容量

也支持预先将集合设置入队列

两把锁,一个take锁,控制消费者并发,一个put锁,控制生产者并发:


内部维护单向链表结构:

来看一下主要方法:offer与poll
offer方法:
如果e为null或者对列已满,返回false, 然后加锁,其他的生产者会被阻塞,再次判断如果对列里面元素数量小于容量,那么入队,对列的数量也自加,
如果这时对列仍然有空间,会唤醒正在等待的其他生产者,向对列里面放数据。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public boolean offer(E e) { if (e == null) throw new NullPointerException(); final AtomicInteger count = this.count; if (count.get() == capacity) return false; int c = -1; Node<E> node = new Node<E>(e); final ReentrantLock putLock = this.putLock; putLock.lock(); try { if (count.get() < capacity) { enqueue(node); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); } } finally { putLock.unlock(); } if (c == 0) signalNotEmpty(); return c >= 0; } |
入队方法:

如果是第一次放入数据,效果图:

主要是建立两个连接,让最后一个元素last指向新来的元素,然后将last指针指向新来的。
再来看一下poll方法:取数据
如果对列为空,返回null ,然后加锁,其他想取数据的消费者线程会被阻塞, 如果没有数据释放锁,返回null,对列有数据,则出队,对列自减,
如果出队后对列中还有数据,那么会唤醒正在等待的其他消费者线程来取数据。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public E poll() { final AtomicInteger count = this.count; if (count.get() == 0) return null; E x = null; int c = -1; final ReentrantLock takeLock = this.takeLock; takeLock.lock(); try { if (count.get() > 0) { x = dequeue(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); } } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); return x; } |
出队方法:

返回first的item元素,这个链表的头结点维护的都是空节点,效果图如下:
出队前:

出队后:

add 和remove:
add方法: 直接使用父类AbstractQueue的方法:
在offer的基础上进行了保证,成功返回true,false的时候返回异常。

remove方法:

两把锁同时上锁,两把锁同时解锁:

来看一下删除元素的动作:因为数据结构是链表,所以只需要把指向该节点的上一个节点的next变量不指向该节点即可,然后
gc的时候就会把该节点回收掉:
trial.next = p.next 的作用就是让p节点的前一个元素直接指向p的后一个元素,而数组结构就是把该下标置为null object[takeIndex] == null

put和take方法:
put方法:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); // Note: convention in all put/take/etc is to preset local var // holding count negative to indicate failure unless set. int c = -1; Node<E> node = new Node<E>(e); final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly(); try { /* * Note that count is used in wait guard even though it is * not protected by lock. This works because count can * only decrease at this point (all other puts are shut * out by lock), and we (or some other waiting put) are * signalled if it ever changes from capacity. Similarly * for all other uses of count in other wait guards. */ while (count.get() == capacity) { notFull.await(); } enqueue(node); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); } finally { putLock.unlock(); } if (c == 0) signalNotEmpty(); } |
take方法:
take方法的判断逻辑与poll基本相同,唯一区别是,如果对列没有元素,take为阻塞消费者线程,而poll会返回false。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { while (count.get() == 0) { notEmpty.await(); } x = dequeue(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); return x; } |
深入LinkedBlockingQueue实现原理的更多相关文章
- 20.并发容器之ArrayBlockingQueue和LinkedBlockingQueue实现原理详解
1. ArrayBlockingQueue简介 在多线程编程过程中,为了业务解耦和架构设计,经常会使用并发容器用于存储多线程间的共享数据,这样不仅可以保证线程安全,还可以简化各个线程操作.例如在“生产 ...
- 理解线程池到走进dubbo源码
引言 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. ...
- 学习笔记 07 --- JUC集合
学习笔记 07 --- JUC集合 在讲JUC集合之前我们先总结一下Java的集合框架,主要包含Collection集合和Map类.Collection集合又能够划分为LIst和Set. 1. Lis ...
- BlockingQueue 解析
阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列 ...
- BlockingQueue深入解析-BlockingQueue看这一篇就够了
本篇将详细介绍BlockingQueue,以下是涉及的主要内容: BlockingQueue的核心方法 阻塞队列的成员的概要介绍 详细介绍DelayQueue.ArrayBlockingQueue.L ...
- 008 BlockingQueue理解
原文https://www.cnblogs.com/WangHaiMing/p/8798709.html 本篇将详细介绍BlockingQueue,以下是涉及的主要内容: BlockingQueue的 ...
- 生产者消费者模型Java实现
生产者消费者模型 生产者消费者模型可以描述为: ①生产者持续生产,直到仓库放满产品,则停止生产进入等待状态:仓库不满后继续生产: ②消费者持续消费,直到仓库空,则停止消费进入等待状态:仓库不空后,继续 ...
- LinkedBlockingQueue出入队实现原理
类图概述 由类图可以看出,L是单向链表实现的,有两个ReentrantLock实例用来控制元素入队和出队的原子性,takeLock用来控制只有一个线程可以从队头获取元素,putLock控制只有一个线程 ...
- 分布式开放消息系统(RocketMQ)的原理与实践
分布式消息系统作为实现分布式系统可扩展.可伸缩性的关键组件,需要具有高吞吐量.高可用等特点.而谈到消息系统的设计,就回避不了两个问题: 消息的顺序问题 消息的重复问题 RocketMQ作为阿里开源的一 ...
- Java并发集合的实现原理
本文简要介绍Java并发编程方面常用的类和集合,并介绍下其实现原理. AtomicInteger 可以用原子方式更新int值.类 AtomicBoolean.AtomicInteger.AtomicL ...
随机推荐
- 利用DbgHelp获取线程的栈回溯信息
当线程发生异常时,我们如果可以记录下来异常线程的堆栈信息,那么对于我们后续问题处理将会有极大的帮助.这里记录一个操作方法. 1 #include <iostream> 2 #include ...
- 解决DDD最大难题-如何划分领域
本文书接上回<反DDD模式之"复用">,关注公众号(老肖想当外语大佬)获取信息: 最新文章更新: DDD框架源码(.NET.Java双平台): 加群畅聊,建模分析.技术 ...
- 多校A层冲刺NOIP2024模拟赛18
多校A层冲刺NOIP2024模拟赛18 T1 选彩笔(rgb) 签到题,但是没签上... 没想到三维前缀和,直接上了个bitset. 就是直接二分答案,然后枚举这三维每维的区间的起点,前缀和查数量是否 ...
- Python 潮流周刊#76:用 50 行 Python 代码实现 BASIC(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- 内核源码+vscode+bear+clang实现函数任意跳转,无缝跳转,无缝阅读,无缝开发
一.准备工作 1.内核源码版本选择 务必有一份能编译通过的<内核源码>,本次选择5.10版本的. #说明:5.10版本的<内核源码>里,在 scripts/clang-tool ...
- js 数字计算的精度问题
〇.js 的数值计算存在结果不精确的情况 最近接触财务相关系统,页面上会有一些简单的计算,就发现其实是非常简单的计算,但 js 计算出来的结果却不是预期值,可能带上一大串 0 或 9,导致计算结果错误 ...
- IPC-7711/7721D-中文版 CN 2024 电子组件的返工、修改和维修标准
IPC-7711D-7721D-中文版 CN 2024 电子组件的返工.修改和维修标准 链接:https://pan.baidu.com/s/1r5dm9vsj4-Oj2Jw0HgeTHw?pwd=1 ...
- Python中dict支持多个key的方法
在Python中,字典(dict)是一种非常强大的数据结构,它允许我们通过键(key)来存储和检索值(value).有时候,我们可能想要根据多个键来检索或操作字典中的数据.虽然Python的字典不直接 ...
- 使用PicGo存储markdown图片(阿里云或者github)
PicGo代替极简图床 之前使用极简床图,但是后来好像挂了,真是一件悲伤的事,最近才发现了一个神器,开源的PicGo,已经有各个平台的版本了.链接如下:https://github.com/Molun ...
- LiV-GS: LiDAR-Vision Integration for 3D Gaussian Splatting SLAM in Outdoor Environments
arxiv |哈工大发布 LiV-GS:户外环境中基于3D高斯泼溅的LiDAR-视觉集成SLAM系统 [LiV-GS: LiDAR-Vision Integration for 3D Gaussian ...