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 ...
随机推荐
- Debian/Ubuntu下安装Apache的Mod_Rewrite模块的步骤分享
启用 Mod_rewrite 模块:sudo a2enmod rewrite 另外,也可以通过将 /etc/apache2/mods-available/rewrite.load 连接到 /etc/a ...
- [LeetCode] 203. 移除链表元素
题目链接:https://leetcode-cn.com/problems/remove-linked-list-elements/ 题目描述: 删除链表中等于给定值 val 的所有节点. 示例: 输 ...
- python day2-爬虫实现github登录
GitHub登录 分析登录页面 开发者工具分析请求 从session请求分析得知: 1.请求的URL为:https://github.com/session 2.该请求为post请求,即需要上传dat ...
- ECCV2014 Accepted paper
今天早上看到小伙伴们说ECCV2014结果出来了, 自己于是赶紧看了下, 感觉ECCV2014显著性的文章和以往的不太一样. 1.Salient Montages from Unconstrained ...
- MySQL第一讲 一一一一 数据库入门
一. MySQL简介与安装 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方 ...
- 出现( linker command failed with exit code 1)错误总结(http://blog.csdn.net/hengshujiyi/article/details/21182813)
这种问题,通常出现在添加第三方库文件或者多人开发时. 这种问题一般是找不到文件而导致的链接错误. 我们可以从如下几个方面着手排查. 1.以如下错误为例,如果是多人开发,你同步完成后发现出现如下的错误. ...
- php 多维数组转一维数组
1.巧用 RecursiveIteratorIterator $k_arrays_arrays_tmp = iterator_to_array(new RecursiveIteratorIterato ...
- Linux之目录与路径
特殊的目录: “.”,代表此层目录 “..”,代表上一层目录 “-”,代表前一个工作目录 “~”,代表“目前用户身份”所在的主文件夹 “~account”,代表account这个用户的主文件夹(acc ...
- airflow 简介
转载:https://zhuanlan.zhihu.com/p/36043468 简介 Apache-Airflow 是Airbnb开源的一款数据流程工具,目前是Apache孵化项目.以非常灵活的方式 ...
- DevExpress v18.2版本亮点——Office File API 篇
行业领先的.NET界面控件——DevExpress v18.2版本亮点详解,本文将介绍了DevExpress Office File API v18.2 的版本亮点,新版30天免费试用!点击下载> ...