深入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 ...
随机推荐
- 每日学学Java开发规范,集合处理(附阿里巴巴Java开发手册(终极版))
前言 每次去不同的公司,码不同的代码,适应不同的规范,经常被老大教育规范问题,我都有点走火入魔的感觉,还是要去看看阿里巴巴Java开发规范,从中熟悉一下,纠正自己,码出高效,码出质量. 想细看的可以去 ...
- 机器学习中验证两个算法之间是否存在显著差距的t-test检验
同一主题的简单分析版本,建议查看: 机器学习领域中假设检验的使用 本文内容为在上文基础上进一步分析版本. 相关: t检验 t检验应用条件 t检验(t-test) t-test终极指南 一文详解t检验 ...
- 基于 Github 平台的 .NET 开源项目模板 - PR 相关
本篇将介绍开源模板的 PR 相关功能配置 赘述 project.yml 使用周期 文件来源: 运行跟目录下的 scanner.bat/sh 生成的. 文件位置: .github\project.yml ...
- git rebase -i的时候用的不是 vi 编辑器是 nano编辑器不会用
今天给同事 rebase 代码 发现 git fetch && git rebase -i origin/develop 的时候 出现了 那个 nano 编辑器的界面 不会用,和vim ...
- BLOG-1
前言 回顾这三次作业的心路历程,可以说每一次都带来了新的挑战与收获,随着题目数量和复杂度的增加,对Java编程的理解和面向对象设计的认知逐步加深.作为Java编程初学者,最初对编程架构.模块分层和错误 ...
- Linux中vim快捷键+vim报错解决
vim快捷键+vim报错解决 vim 快捷键 编辑器 yum -y install vim 快捷键: 视图模式: 0 Home ^ 快速移动光标到行首 $ End 快速移动光标到行尾 u 撤销 ...
- 常见Java面试题 – 第三部分:重载(overloading)与重写(overriding)
ImportNew注: 本文是ImportNew编译整理的Java面试题系列文章之一.你可以从这里查看全部的Java面试系列. 这篇文章介绍的常见面试题是关于重载(overloading)方法和重写( ...
- ssh之秘钥登陆
前提: 1. 秘钥的生成需要OpenSSL的支持, 需要自行进行安装 一. 新建用户 在root登陆状态中执行命令: useradd -m ssh-user # centosadduser ssh-u ...
- Blazor 组件库 BootstrapBlazor 中Modal组件介绍
组件说明 Model组件是一个模态框组件,可以弹出一个对话框,适合需要定制性更大的场景. 它的样子如下: 其html代码为: <div class="modal-content&quo ...
- RocketMQ系列1:基础介绍
★消息队列16篇 1 认识RocketMQ RocketMQ是一款基于Java开发的分布式消息中间件,它以其高性能.高可靠性.高实时性以及分布式特性而广受好评. 它支持事务消息.顺序消息.批量消息.定 ...