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 ...
随机推荐
- c语言中字符串跨行书写的问题
字符串常量定义时的换行问题 如果我们在一行代码的行尾放置一个反斜杠,c语言编译器会忽略行尾的换行符,而把下一行的内容也算作是本行的内容.这里反斜杠起到了续行的作用. 如果我们不使 ...
- [LeetCode] 132. 分割回文串 II
题目链接 : https://leetcode-cn.com/problems/palindrome-partitioning-ii/ 题目描述: 给定一个字符串 s,将 s 分割成一些子串,使每个子 ...
- HBase Shell 的常用操作总结
1,创建表:create 't1','f1','f2','f3' #-------t1是表名,f1,f2,f3是列族名 2,查看所有的表:list 3, ...
- 自动清理ES索引脚本
#/bin/bash #指定日期(3个月前) DATA=`date -d "3 month ago" +%Y.%m.%d` #当前日期 time=`date` #删除3个月前的日志 ...
- 【问题解决方案】Centos操作文件vim-No write since last change (add ! to override)
参考链接 CSDN:Centos 7 操作文件No write since last change (add ! to override) 问题描述: :q或者:wq退出失败,显示如No write ...
- react native 在vscode上运行
1.在用react-native init xxx 创建rn项目之后,在Android目录中创建local.properties文件 =后面接上sdk地址 2.react-native start 命 ...
- python 查询Neo4j多节点的多层关系
需求:查询出满足3人及3案有关系的集合 # -*- coding: utf-8 -*- from py2neo import Graph import psycopg2 # 二维数组查找 def fi ...
- iOS 审核app被拒绝的各种理由以及翻译
原 apps被拒绝的各种理由以及翻译:http://my.oschina.net/201003674/blog/356189#OSC_h1_3 1. Terms and conditions(法律与条 ...
- /proc/sys/fs/file-max
Linux的/proc/sys/fs/file-max决定了当前内核可以打开的最大的文件句柄数. 查看当前的值: cat /proc/sys/fs/file-max 这个值在kernel的文档里是这样 ...
- 北京师范大学第十五届ACM决赛-重现赛E Euclidean Geometry (几何)
链接:https://ac.nowcoder.com/acm/contest/3/E 来源:牛客网 Euclidean Geometry 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ ...