BlockingQueue的几个实现分析
ArrayBlockingQueue
底层以数组的结构存放队列元素,容量大小不可改变。
先看下变量:

items:数组,用于存放队列中的元素
takeIndex:获取元素的索引位置
putIndex:存放元素的索引位置
count:队列中当前元素数量
lock:控制队列进出的锁,ArrayBlockingQueue中进出共用一把锁,LinkedBlockingQueue中进出是两把分开的锁
notEmpty:从队列获取元素时的等待条件,也就是队列空时,获取元素的线程会阻塞,直到有线程放元素进来,notEmpty.signal唤醒
notFull:往队列里放元素时的等待条件,也就是队列满时,存放元素的线程会阻塞,直到有线程从队列里取元素并调用notFull.signal唤醒
下面看下代码更直观些:
put

1.先获得锁
2.循环判断当前元素数量是否等于数组长度,也就是队列的容量,等于的话就挂起当前线程,直到notFull.signal
3.插入元素到队列中,代码如下:

putIndex为下一次入队的索引位置,先把元素入队,然后+1后判断是否等于数组长度,如果等于就置位0,从0开始
最后count+1并notEmpty.signal唤醒因为队列为空导致获取元素阻塞的线程。
take

1.获取锁,注意,是和put同一把锁,也就是同时只能有一个线程获得操作队列的权限,无论是入队还是出队。
2.循环判断如果此时队空,就挂起当前线程,等待notEmpty.signal
3.出队:

获取元素并将数组中相应位置置空,设置下次入队索引并count-1,notFull.signal唤醒因为队满而阻塞的线程并返回出队元素
LinkedBlockingQueue
底层以单向链表结构存放队列元素,put和take分别采用两把不同的锁,也就是读写是并发运行的,
那么怎样控制并发环境下的出队和入队呢?答案就是使用AtomicInteger原子操作当前队列中元素数量。
下面看下有哪些实例变量:

capacity:队列容量,可以在构造方法中设置容量,如果不设置的话,默认为Integer.MAX_VALUE
count:当前队列中的元素数量,AtomicInteger类型,因为这里的put和take是两把锁,也就是会并发修改count,所以这里采用原子操作。
head:队头
last:队尾
takeLock:出队锁
notEmpty:出队时,如果队空,就等待notEmpty.signal
putLock:入队锁
notFull:入队时,如果队满,就等待notFull.signal
put
public void put(E e) throws InterruptedException {
// 因为在出队时如果队空,有些方法是非阻塞或者等待一段时间后返回null的,比如poll
// 所以禁止往队列中入队null
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对象中
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.
*/
// 因为count加操作都在putLock中,所以这里可以是==判断
while (count.get() == capacity) {
notFull.await();
}
// 入队
enqueue(node);
// 获取原来count值并原子加一
c = count.getAndIncrement();
// 如果队列未满,通知阻塞线程继续入队
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
// 如果队列之前是队空状态,说明之前有可能存在take元素的线程因为队空而阻塞,这里通知takeLock唤醒等待线程
if (c == 0)
signalNotEmpty();
}

很简单,就是将刚刚的node链接到队尾
take

1.先获得takeLock
2.如果队空,挂起等待
3.出队
4.获取count值并减一
5.如果队列中还有元素,notEmpty唤醒之前因为队空阻塞的线程
6.如果take之前状态为队满,说明有可能存在因为队满而导致入队阻塞的线程,
唤醒他们(其实这里signal只唤醒一个,在被唤醒的线程完成入队后判断如果还有空间就继续signal,是这样一连串的动作)

出队逻辑如上,保持head的item为null,这里需要说明的是h.next = h自身循环引用也是会被GC的
SynchronousQueue
SynchronousQueue本身不存储元素,put元素时必须同步等待元素被取走。
PriorityBlockingQueue
具有优先级的队列
DelayQueue
具有延时的队列,延时不结束就不能取数据
BlockingQueue的几个实现分析的更多相关文章
- java线程池技术(一):ThreadFactory与BlockingQueue
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.ThreadFactory概述以及源码分析 ThreadFactory很简单,就是一个线程工厂也就是负责生产线程的,我们看下ThreadFact ...
- Java多线程(六) —— 线程并发库之并发容器
参考文献: http://www.blogjava.net/xylz/archive/2010/07/19/326527.html 一.ConcurrentMap API 从这一节开始正式进入并发容器 ...
- 深入浅出 Java Concurrency (20): 并发容器 part 5 ConcurrentLinkedQueue[转]
ConcurrentLinkedQueue是Queue的一个线程安全实现.先来看一段文档说明. 一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素进行排序.队列的头部 是队 ...
- ArrayBlockingQueue,BlockingQueue分析
BlockingQueue接口定义了一种阻塞的FIFO queue,每一个BlockingQueue都有一个容量,让容量满时往BlockingQueue中添加数据时会造成阻塞,当容量为空时取元素操作会 ...
- Java并发包分析——BlockingQueue
之前因为找实习的缘故,博客1个多月没有写了.找实习的经历总算告一段落,现在重新更新博客,这次的内容是分析Java并发包中的阻塞队列 关于阻塞队列,我之前是一直充满好奇,很好奇这个阻塞是怎么实现.现在我 ...
- java并发包——阻塞队列BlockingQueue及源码分析
一.摘要 BlockingQueue通常用于一个线程在生产对象,而另外一个线程在消费这些对象的场景,例如在线程池中,当运行的线程数目大于核心的线程数目时候,经常就会把新来的线程对象放到Blocking ...
- java并发包分析之———BlockingQueue
一.概述: BlockingQueue作为线程容器,可以为线程同步提供有力的保障. 二.BlockingQueue定义的常用方法 1.BlockingQueue定义的常用方法如下: 抛出异常 ...
- JDK源码分析(11)之 BlockingQueue 相关
本文将主要结合源码对 JDK 中的阻塞队列进行分析,并比较其各自的特点: 一.BlockingQueue 概述 说到阻塞队列想到的第一个应用场景可能就是生产者消费者模式了,如图所示: 根据上图所示,明 ...
- 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析
LinkedBlockingQueue 在看源码之前,通过查询API发现对LinkedBlockingQueue特点的简单介绍: 1.LinkedBlockingQueue是一个由链表实现的有界队列阻 ...
随机推荐
- NABCD model作业
1)N(Need需求) 随着人类生活的快速发展,给人们带来了许多的便利,同时也给我们带来了一些麻烦,而我的拼图这个小游戏可以在人们在无聊时玩一玩,也可以给小孩子开发智力. 2)A(Approach做法 ...
- Git - Windows 下, gitbash 打开资源管理器
1. 概述 windows 下 gitbash 打开 资源管理器 2. 场景 资源管理唤起 gitbash 步骤 进入目录 鼠标右击 在 弹出菜单 中, 找到 Git Bash Here 结果 打开一 ...
- Git - 03. git 工作空间
1. 概述 git 存放代码的地方 2. 创建 命令 # 1. 从无到有 > git init # 2. 从远程拉去现有的仓库 > git clone <url> 3. 文件生 ...
- 每天进步一点点------时序分析基础与时钟约束实例(四)IO口时序(Input Delay /output Delay)
1.1 概述 在高速系统中FPGA时序约束不止包括内部时钟约束,还应包括完整的IO时序约束和时序例外约束才能实现PCB板级的时序收敛.因此,FPGA时序约束中IO口时序约束也是一个重点.只有约束正确 ...
- jmeter实现服务器端后台接口性能测试
实现目的 在进行服务器端后台接口性能测试时,需要连接到Linux服务器端,然后通过命令调用socket接口,这个过程就需要用到jmeter的SSH Command取样器实现了. 脚本实现 设置CSV ...
- Gitlab+Jenkins用钩子实现git提交后jenkins自动化构建
Gitlab+Jenkins用钩子实现git提交后jenkins自动化构建 一.Jenkins 进入项目---->Configure 1.设置项目代码从git中拉取 2.设置钩子程序 二.git ...
- 不需要图片,css+svg绘制动态loading加载图标
1.html 部分: <div id="refershDiv" class="refershDiv"> <svg xmlns="ht ...
- Spring Boot 操作 Excel
Excel 在日常操作中经常使用到,Spring Boot 中使用 POI 操作 Excel 本项目源码 github 下载 1 新建 Spring Boot Maven 示例工程项目 注意:本示例是 ...
- android实现扫一扫等功能
链接:https://blog.csdn.net/weixin_40399313/article/details/78814325
- 吴裕雄 python 神经网络——TensorFlow 训练过程的可视化 TensorBoard的应用
#训练过程的可视化 ,TensorBoard的应用 #导入模块并下载数据集 import tensorflow as tf from tensorflow.examples.tutorials.mni ...