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是一个由链表实现的有界队列阻 ...
随机推荐
- .h头文件 .lib库文件 .dll动态链接库文件关系
.h头文件是编译时必须的,lib是链接时需要的,dll是运行时需要的. 附加依赖项的是.lib不是.dll,若生成了DLL,则肯定也生成 LIB文件.如果要完成源代码的编译和链接,有头文件和lib就够 ...
- golang采坑记录
安装golang,引入第三方库,采坑 1.获取安装包 go语言中文网:https://studygolang.com/dl 官网地址:https://studygolang.com/dl 2.下载 选 ...
- nodejs--wechat模块,微信公共平台自动回复消息接口服务中间件
npm 程序官网 https://www.npmjs.com/package/wechat 别人使用笔记网址 https://blog.csdn.net/golduty2/article/detail ...
- CSS学习(2)Id和Class选择器
id 选择器 id 选择器可以为标有特定 id 的 HTML 元素指定特定的样式. HTML元素以id属性来设置id选择器,CSS 中 id 选择器以 "#" 来定义. 以下的样式 ...
- CF1067C Knights 构造
题目链接:https://codeforc.es/contest/1067/problem/C 题意:有一个无限大的棋盘,棋盘上初始放置了\(n\)个国际象棋马.如果某一个格子没有放马且能够被\(4\ ...
- C:字符数组 与 字符串
字符串与字符数组 C语言中没有字符串这种数据类型,可以通过char的数组来替代: 字符串一定是一个char的数组,但char的数组未必是字符串: 数字0(和字符'\0'等价)结尾的char数组就是一个 ...
- 解决:jenkins jnlp安装 provided port 40127 is not reachable
解决方法: 开放linux 防火墙40127端口允许下载jnlp centos7 解决如下: [root@hostuser chrome]# firewall-cmd --zone=public -- ...
- JS-条件语句5准则
准则: 1.多重判断时使用 Array.includes 2.更少的嵌套,尽早 return 3.使用默认参数和解构 4.倾向于遍历对象而不是 Switch 语句 5.对 所有/部分 判断使用 Arr ...
- ABC156 F - Modularness
题目链接 题意还是比较清楚的,给你q个询问,对每组询问的模数和初始值不同,求满足条件\(a_j~\textrm{mod}~m_i < a_{j + 1}~\textrm{mod}~m_i,(0 ...
- C语言笔记 11_头文件&强制类型转换&错误处理&递归
头文件 头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享.有两种类型的头文件:程序员编写的头文件和编译器自带的头文件. 在程序中要使用头文件,需要使用 C 预处理指 ...