LinkedBlockingDeque 源码分析
LinkedBlockingDeque
LinkedBlockingDeque 能解决什么问题?什么时候使用 LinkedBlockingDeque?
1)LinkedBlockingDeque 是基于双向链表实现的,可以选择有界或无界的双端阻塞队列。
2)LinkedBlockingDeque 使用相同的互斥锁来保证线程安全性,读写操作性能低于 LinkedBlockingQueue。
如何使用 LinkedBlockingDeque?
1)并发场景下,需要作为双端队列使用时,如果只是作为 FIFO 队列使用,则 LinkedBlockingQueue 的性能更高。
2)指定队列的容量,以避免生产速率远高于消费速率时资源耗尽的问题。
使用 LinkedBlockingDeque 有什么风险?
1)未指定容量的情况下,生产速率远高于消费速率时,会导致内存耗尽而 OOM。
2)高并发场景下,性能远低于 LinkedBlockingQueue。
3)由于需要维持前后节点的链接,内存消耗也高于 LinkedBlockingQueue。
LinkedBlockingDeque 核心操作的实现原理?
- 创建实例
/** 双向链表节点 */
static final class Node<E> {
/**
* 节点元素,如果节点已经被移除,则为 null
*/
E item;
/**
* One of:
* - the real predecessor Node
* - this Node, meaning the predecessor is tail
* - null, meaning there is no predecessor
*/
Node<E> prev;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head
* - null, meaning there is no successor
*/
Node<E> next;
Node(E x) {
item = x;
}
}
/**
* 头结点
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* 尾节点
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
/** 双端队列中的元素总数 */
private transient int count;
/** 双端队列的容量 */
private final int capacity;
/** 控制访问的锁 */
final ReentrantLock lock = new ReentrantLock();
/** 队列为空时,用于阻塞执行 take 操作的线程的非空条件 */
private final Condition notEmpty = lock.newCondition();
/** 队列已满时,用于阻塞执行 put 操作的线程的非满条件 */
private final Condition notFull = lock.newCondition();
/**
* 创建一个容量为 Integer.MAX_VALUE 的双端阻塞队列
*/
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
/**
* 创建一个容量为 capacity 的双端阻塞队列
*/
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException();
}
this.capacity = capacity;
}
- 将目标元素 e 添加到队列头部,如果队列已满,则阻塞等待有可用空间后重试
/**
* 将目标元素 e 添加到队列头部,如果队列已满,则阻塞等待有可用空间后重试
*/
public void putFirst(E e) throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
final Node<E> node = new Node<>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 尝试在头部添加元素
while (!linkFirst(node)) {
// 当前线程在非满条件上等待
notFull.await();
}
} finally {
lock.unlock();
}
}
private boolean linkFirst(Node<E> node) {
// 队列已满,则直接返回 false
if (count >= capacity) {
return false;
}
// 读取头节点
final Node<E> f = first;
// 将旧头结点链接到目标节点之后
node.next = f;
// 写入新头节点
first = node;
// 1)当前元素为第一个添加到队列中的元素
if (last == null) {
// 写入尾节点
last = node;
} else {
// 将旧头节点的前置节点设置为新头结点
f.prev = node;
}
// 递增计数
++count;
// 唤醒在非空条件上阻塞等待的线程来读取元素
notEmpty.signal();
return true;
}
- 如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列头部
/**
* 如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列头部
*/
@Override
public boolean offerFirst(E e) {
if (e == null) {
throw new NullPointerException();
}
final Node<E> node = new Node<>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkFirst(node);
} finally {
lock.unlock();
}
}
- 在指定的超时时间内尝试将目标元素 e 添加到队列头部,成功则返回 true
/**
* 在指定的超时时间内尝试将目标元素 e 添加到队列头部,成功则返回 true
*/
@Override
public boolean offerFirst(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
final Node<E> node = new Node<>(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 头结点添加失败
while (!linkFirst(node)) {
// 已经超时则直接返回
if (nanos <= 0L) {
return false;
}
// 当前线程在非满条件上阻塞等待,唤醒后再次尝试添加
nanos = notFull.awaitNanos(nanos);
}
return true;
} finally {
lock.unlock();
}
}
- 将目标元素 e 添加到队列尾部,如果队列已满,则阻塞等待有可用空间后重试
/**
* 将目标元素 e 添加到队列尾部,如果队列已满,则阻塞等待有可用空间后重试
*/
@Override
public void putLast(E e) throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
final Node<E> node = new Node<>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 尝试将节点链接到队列尾部
while (!linkLast(node)) {
// 队列已满,当前线程在非满条件上阻塞等待,被唤醒后再次尝试
notFull.await();
}
} finally {
lock.unlock();
}
}
private boolean linkLast(Node<E> node) {
// 队列已满,则直接返回 false
if (count >= capacity) {
return false;
}
// 读取尾节点
final Node<E> l = last;
// 将目标节点链接到尾节点之后
node.prev = l;
// 写入尾节点为新增节点
last = node;
// 1)当前元素是第一个加入队列的元素
if (first == null) {
// 写入头结点
first = node;
} else {
// 将旧尾节点的后置节点更新为新增节点
l.next = node;
}
// 递增总数
++count;
// 唤醒在非空条件上等待的线程
notEmpty.signal();
return true;
}
- 如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列尾部
/**
* 如果队列已满,则直接返回 false,否则将目标元素 e 添加到队列尾部
*/
@Override
public boolean offerLast(E e) {
if (e == null) {
throw new NullPointerException();
}
final Node<E> node = new Node<>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkLast(node);
} finally {
lock.unlock();
}
}
- 在指定的超时时间内尝试将目标元素 e 添加到队列尾部,成功则返回 true
/**
* 在指定的超时时间内尝试将目标元素 e 添加到队列尾部,成功则返回 true
*/
@Override
public boolean offerLast(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
final Node<E> node = new Node<>(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 尝试将目标元素 e 添加到队列尾部
while (!linkLast(node)) {
// 已经超时则直接返回 false
if (nanos <= 0L) {
return false;
}
// 当前线程在非满条件上阻塞等待,被唤醒后再次尝试
nanos = notFull.awaitNanos(nanos);
}
return true;
} finally {
lock.unlock();
}
}
- 移除并返回头部节点,如果队列为空,则阻塞等待有可用元素之后重试
/**
* 移除并返回头部节点,如果队列为空,则阻塞等待有可用元素之后重试
* created by ZXD at 6 Dec 2018 T 21:00:25
* @return
* @throws InterruptedException
*/
@Override
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
// 尝试移除并返回头部节点
while ( (x = unlinkFirst()) == null) {
// 队列为空,则阻塞等待有可用元素之后重试
notEmpty.await();
}
return x;
} finally {
lock.unlock();
}
}
- 移除并返回尾部节点,如果队列为空,则阻塞等待有可用元素之后重试
/**
* 移除并返回尾部节点,如果队列为空,则阻塞等待有可用元素之后重试
* created by ZXD at 6 Dec 2018 T 21:02:04
* @return
* @throws InterruptedException
*/
@Override
public E takeLast() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
// 尝试移除并返回尾部节点
while ( (x = unlinkLast()) == null) {
// 队列为空,则阻塞等待有可用元素之后重试
notEmpty.await();
}
return x;
} finally {
lock.unlock();
}
}
- 如果队列为空,则立即返回 null,否则移除并返回头部元素
/**
* 如果队列为空,则立即返回 null,否则移除并返回头部元素
* created by ZXD at 6 Dec 2018 T 21:03:40
* @return
*/
@Override
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
} finally {
lock.unlock();
}
}
- 如果队列为空,则立即返回 null,否则移除并返回尾部元素
/**
* 如果队列为空,则立即返回 null,否则移除并返回尾部元素
* created by ZXD at 6 Dec 2018 T 21:04:43
* @return
*/
@Override
public E pollLast() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkLast();
} finally {
lock.unlock();
}
}
- 在指定的超时时间内尝试移除并返回头部元素,如果已经超时,则返回 null
/**
* 在指定的超时时间内尝试移除并返回头部元素,如果已经超时,则返回 null
* created by ZXD at 6 Dec 2018 T 21:05:21
* @param timeout
* @param unit
* @return
* @throws InterruptedException
*/
@Override
public E pollFirst(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
E x;
// 尝试移除并返回头部元素
while ( (x = unlinkFirst()) == null) {
// 已经超时则返回 null
if (nanos <= 0L) {
return null;
}
// 当前线程在非空条件上阻塞等待,被唤醒后进行重试
nanos = notEmpty.awaitNanos(nanos);
}
// 移除成功则直接返回头部元素
return x;
} finally {
lock.unlock();
}
}
- 在指定的超时时间内尝试移除并返回尾部元素,如果已经超时,则返回 null
/**
* created by ZXD at 6 Dec 2018 T 21:08:24
* @param timeout
* @param unit
* @return
* @throws InterruptedException
*/
@Override
public E pollLast(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
E x;
// 尝试移除并返回尾部元素
while ( (x = unlinkLast()) == null) {
// 已经超时则返回 null
if (nanos <= 0L) {
return null;
}
// 当前线程在非空条件上阻塞等待,被唤醒后进行重试
nanos = notEmpty.awaitNanos(nanos);
}
// 移除成功则直接返回尾部元素
return x;
} finally {
lock.unlock();
}
}
LinkedBlockingDeque 源码分析的更多相关文章
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- lesson2:java阻塞队列的demo及源码分析
本文向大家展示了java阻塞队列的使用场景.源码分析及特定场景下的使用方式.java的阻塞队列是jdk1.5之后在并发包中提供的一组队列,主要的使用场景是在需要使用生产者消费者模式时,用户不必再通过多 ...
- Spark Scheduler模块源码分析之DAGScheduler
本文主要结合Spark-1.6.0的源码,对Spark中任务调度模块的执行过程进行分析.Spark Application在遇到Action操作时才会真正的提交任务并进行计算.这时Spark会根据Ac ...
- 【java多线程】队列系统之LinkedBlockingDeque源码
1.简介 上一篇我们介绍了 LinkedBlockingDeque 的兄弟篇 LinkedBlockingQueue .听名字也知道一个实现了 Queue 接口,一个实现了 Deque 接口,由于 D ...
- 细说并发5:Java 阻塞队列源码分析(下)
上一篇 细说并发4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...
- Spark源码分析之二:Job的调度模型与运行反馈
在<Spark源码分析之Job提交运行总流程概述>一文中,我们提到了,Job提交与运行的第一阶段Stage划分与提交,可以分为三个阶段: 1.Job的调度模型与运行反馈: 2.Stage划 ...
- JUC源码分析-集合篇:并发类容器介绍
JUC源码分析-集合篇:并发类容器介绍 同步类容器是 线程安全 的,如 Vector.HashTable 等容器的同步功能都是由 Collections.synchronizedMap 等工厂方法去创 ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
随机推荐
- 开发jquery插件小结
用jquery开发插件其实很简单.今天实现了一个入门级别的功能. 随便来个DIV,便于理解. div{ height:100px;width:100px;display:block;backgroun ...
- 计算机系统结构总结_Instruction Set Architecture
Textbook:<计算机组成与设计——硬件/软件接口> HI<计算机体系结构——量化研究方法> QR 这节我们来看CPU内部的一些东西. Instruct ...
- 执行命令npm publish报错:403 Forbidden - PUT https://registry.npmjs.org/kunmomotest2 - You cannot publish over the previously published versions: 0.0.1.
前言 执行命令npm publish报错:403 Forbidden - PUT https://registry.npmjs.org/kunmomotest2 - You cannot publis ...
- ambari 2.5.0源码编译安装
参考:https://www.ibm.com/developerworks/cn/opensource/os-cn-bigdata-ambari/index.html Ambari 是什么 Ambar ...
- IO同步阻塞与同步非阻塞
BIO.NIO.AIO IO(BIO)和NIO区别:其本质就是阻塞和非阻塞的区别 阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,就会一直等待,直到传输完毕为止. 非阻塞概念:应用程序直 ...
- Django rest_frameword 之项目流程
后端开发软件目录规范 一.Model from django.db import models # Create your models here. # 多表的设计 # 图书 作者 出版社 作者详情表 ...
- mysql分组查询及其测试用例
语法: select 查询列表 from 表 [where 筛选条件] group by 分组的字段 [order by 排序的字段]; 特点: 1.和分组函数一同查询的字段必须是group by后出 ...
- zookeeper分布式之学习搭建
一.下载: 下载地址:https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/ 下载解压到 C:\Users\Administrator\Desk ...
- 10年前文章_UC3A/B 开发环境设置
大部分设置和 Z32U 交叉编译环境的配置 类似 Windows 环境 步骤二: 安装 toolchain 和mkII lite V2 的驱动 安装运行 avr32-gnu-toolchain-2.0 ...
- linux添加头文件路径
gcc demo.c -o demo -I/tools/libevent/include -L/tools/libevent/lib -levent -I:头文件目录 -L:静态库目录 -l:静态库 ...