LinkedBlockingQueue 源码分析
LinkedBlockingQueue
LinkedBlockingQueue 是基于链表实现的,可以选择有界或无界的阻塞队列。
队列的元素按照 FIFO 的顺序访问,新增元素添加到队列尾部,移除元素从队列头部开始。
队列头部通过 takeLock 进行并发控制,队列尾部通过 putLock 进行并发控制,
该队列最多可以有两个线程同时操作,其吞吐量要高于 ArrayBlockQueue。
创建实例
/**
* 单向链表节点
*/
static class Node<E> {
/**
* 存储的元素
*/
E item;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head.next
* - null, meaning there is no successor (this is the last node)
*/
Node<E> next;
Node(E x) { item = x; }
}
/** 阻塞队列的容量,如果不指定,则为 Integer.MAX_VALUE */
private final int capacity;
/** 当前的元素总数 */
private final AtomicInteger count = new AtomicInteger();
/**
* 链表头结点
* Invariant: head.item == null
*/
transient Node<E> head;
/**
* 链表尾节点
* Invariant: last.next == null
*/
private transient Node<E> last;
/** 执行读取操作时必须持有的锁 */
private final ReentrantLock takeLock = new ReentrantLock();
/** 队列为空时执行的 take 操作,当前线程将在此条件阻塞等待 */
private final Condition notEmpty = takeLock.newCondition();
/** 执行写入操作时必须持有的锁 */
private final ReentrantLock putLock = new ReentrantLock();
/** 队列已满时执行的 put 操作,当前线程将在此条件阻塞等待 */
private final Condition notFull = putLock.newCondition();
/**
* 创建一个容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue 实例
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* 创建一个容量为 capacity 的 LinkedBlockingQueue 实例
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException();
}
this.capacity = capacity;
last = head = new Node<>(null);
}
写入元素
- 在队列尾部插入元素,如果队列已满,则阻塞等待有可用空间之后,再尝试插入
/**
* 在队列尾部插入元素,如果队列已满,则阻塞等待有可用空间之后,再尝试插入。
*/
@Override
public void put(E e) throws InterruptedException {
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;
// 创建新节点
final Node<E> node = new Node<>(e);
// 读取写锁
final ReentrantLock putLock = this.putLock;
// 读取元素计数值
final AtomicInteger count = this.count;
// 可中断地锁定
putLock.lockInterruptibly();
try {
// 如果当前队列已满
while (count.get() == capacity) {
// 则在非满条件上阻塞等待,线程被唤醒后进行重试
notFull.await();
}
// 加入队列尾部
enqueue(node);
// 递增元素总数
c = count.getAndIncrement();
// 如果添加元素之后还有可用空间
if (c + 1 < capacity) {
// 唤醒在非满条件上阻塞等待的线程
notFull.signal();
}
} finally {
// 释放写锁
putLock.unlock();
}
// 如果是添加的第一个元素
if (c == 0) {
// 唤醒在非空条件上阻塞等待的线程来读取元素
signalNotEmpty();
}
}
/**
* 将节点插入队列尾部
*/
private void enqueue(Node<E> node) {
last = last.next = node;
}
/**
* Signals a waiting take. Called only from put/offer
* 唤醒一个等待读取元素的线程
*/
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
- 如果队列有可用空间,则将元素添加到队列尾部,并返回 true;如果队列已满,则立即返回 false,元素被丢弃。
/**
* 如果队列有可用空间,则将元素添加到队列尾部,并返回 true;
* 如果队列已满,则立即返回 false,元素被丢弃。
*/
@Override
public boolean offer(E e) {
if (e == null) {
throw new NullPointerException();
}
final AtomicInteger count = this.count;
// 队列已满
if (count.get() == capacity) {
// 返回 false
return false;
}
int c = -1;
// 新建节点
final Node<E> node = new Node<>(e);
// 读取写锁
final ReentrantLock putLock = this.putLock;
// 获取锁
putLock.lock();
try {
// 进行二次判断
if (count.get() < capacity) {
// 将节点加入到队列尾部
enqueue(node);
// 递增元素总个数
c = count.getAndIncrement();
if (c + 1 < capacity) {
notFull.signal();
}
}
} finally {
putLock.unlock();
}
if (c == 0) {
signalNotEmpty();
}
return c >= 0;
}
- 在指定的超时时间内,尝试将元素插入到队列尾部,插入成功返回 true
/**
* 在指定的超时时间内,尝试将元素插入到队列尾部,插入成功返回 true
*/
@Override
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
// 转换为纳秒
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
// 队列已满
while (count.get() == capacity) {
// 已经超时则直接返回 false
if (nanos <= 0L) {
return false;
}
// 最多阻塞等待指定的超时时间后再次尝试添加元素
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<>(e));
c = count.getAndIncrement();
if (c + 1 < capacity) {
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0) {
signalNotEmpty();
}
return true;
}
读取元素
- 如果队列为空,则阻塞等待有可用元素,否则移除并获取队列头部元素
/**
* 如果队列为空,则阻塞等待有可用元素,否则移除并获取队列头部元素
* created by ZXD at 6 Dec 2018 T 20:20:15
* @return
* @throws InterruptedException
*/
@Override
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 队列为空
while (count.get() == 0) {
// 在非空条件上阻塞等待,线程被唤醒后再次尝试读取
notEmpty.await();
}
// 移除队列头部元素
x = dequeue();
// 递减总数
c = count.getAndDecrement();
// 如果还有元素可用
if (c > 1) {
// 唤醒在非空条件上阻塞等待的线程
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
// 如果有可用空间
if (c == capacity) {
// 唤醒在非满条件上阻塞等待的线程来插入元素
signalNotFull();
}
return x;
}
/**
* 移除并返回头部节点元素
*/
private E dequeue() {
// 读取头节点
final Node<E> h = head;
// 读取后继节点
final Node<E> first = h.next;
// 清除旧头结点
h.next = h; // help GC
// 写入新头节点
head = first;
// 读取元素值
final E x = first.item;
// 清除头结点的元素值,它只作为一个标记节点
first.item = null;
// 返回元素值
return x;
}
- 如果队列为空,则直接返回 null,否则尝试移除并返回头部元素
/**
* 如果队列为空,则直接返回 null,否则尝试移除并返回头部元素
* created by ZXD at 6 Dec 2018 T 20:23:51
* @return
*/
@Override
public E poll() {
final AtomicInteger count = this.count;
// 队列为空时直接返回 null
if (count.get() == 0) {
return null;
}
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// 有元素可用
if (count.get() > 0) {
// 移除并返回头部元素
x = dequeue();
c = count.getAndDecrement();
if (c > 1) {
notEmpty.signal();
}
}
} finally {
takeLock.unlock();
}
if (c == capacity) {
signalNotFull();
}
return x;
}
- 尝试在指定的超时时间内移除并返回头部元素,如果已经超时,则返回 null
/**
* 尝试在指定的超时时间内移除并返回头部元素,如果已经超时,则返回 null
* created by ZXD at 6 Dec 2018 T 20:27:27
* @param timeout
* @param unit
* @return
* @throws InterruptedException
*/
@Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 队列为空
while (count.get() == 0) {
// 已经超时则直接返回 null
if (nanos <= 0L) {
return null;
}
// 在非空条件上阻塞等待指定的纳秒数,被唤醒后再次进行读取
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1) {
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity) {
signalNotFull();
}
return x;
}
LinkedBlockingQueue 源码分析的更多相关文章
- LinkedBlockingQueue源码分析
1. LinkedBlockingQueue源码分析(JDK8) 2. LinkedBlockingQueue源码分析 啦啦啦
- 死磕 java集合之LinkedBlockingQueue源码分析
问题 (1)LinkedBlockingQueue的实现方式? (2)LinkedBlockingQueue是有界的还是无界的队列? (3)LinkedBlockingQueue相比ArrayBloc ...
- 并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析
LinkedBlockingQueue 在看源码之前,通过查询API发现对LinkedBlockingQueue特点的简单介绍: 1.LinkedBlockingQueue是一个由链表实现的有界队列阻 ...
- java并发编程基础-ReentrantLock及LinkedBlockingQueue源码分析
ReentrantLock是一个较为常用的锁对象.在上次分析的uil开源项目中也多次被用到,下面谈谈其概念和基本使用. 概念 一个可重入的互斥锁定 Lock,它具有与使用 synchronized 相 ...
- 并发队列ConcurrentLinkedQueue与LinkedBlockingQueue源码分析与对比
目录 前言 ConcurrentLinkedQueue 使用方法 存储结构 初始化 入队 出队 获取容器元素数量 LinkedBlockingQueue 使用方法 存储结构 初始化 入队 出队 获取容 ...
- Java核心复习——J.U.C LinkedBlockingQueue源码分析
参考文档 LinkedBlockingQueue和ArrayBlockingQueue的异同
- 【JUC】JDK1.8源码分析之LinkedBlockingQueue(四)
一.前言 分析完了ArrayBlockingQueue后,接着分析LinkedBlockingQueue,与ArrayBlockingQueue不相同,LinkedBlockingQueue底层采用的 ...
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...
- JDK源码分析(11)之 BlockingQueue 相关
本文将主要结合源码对 JDK 中的阻塞队列进行分析,并比较其各自的特点: 一.BlockingQueue 概述 说到阻塞队列想到的第一个应用场景可能就是生产者消费者模式了,如图所示: 根据上图所示,明 ...
随机推荐
- ☆☆☆☆☆Placeholder兼容各大浏览器的例子☆☆☆☆☆
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- 使用form表单提交请求如何获取后台返回的数据?
问题描述 一般的form表单提交是单向的:只能给服务器发送数据,但是无法获取服务器返回的数据,也就是无法读取HTTP应答包. 想要真正的半双工通讯一般需要使用Ajax, 但是Ajax对文件传输也很麻烦 ...
- Jmeter学习总结
学习内容: 1.用户定义的变量 作用:多个地方使用同一个值,且该值在不同的环境下不同,方便脚本在不同环境下运行时修改. 2.基本的HTTP请求,请求方式:get 3.传入参数为json 4.HTTP信 ...
- React全家桶入门
http://blog.csdn.net/column/details/14545.html
- losetup - 设 定 与 控 制 环回设备
总览 SYNOPSIS losetup [ -e encryption ] [ -o offset ] loop_device file losetup [ -d ] loop_device 描述 l ...
- Docker学习笔记--传送门(持续更新)
1.ubuntu下安装docker: https://www.cnblogs.com/salmonLeeson/p/11609699.html 2.为docker配置国内镜像加速器:https:// ...
- AtCoder Regular Contest 066 E - Addition and Subtraction Hard (结论+DP)
Time limit : 2sec / Memory limit : 256MB Score : 900 points Problem Statement Joisino has a formula ...
- 北京师范大学第十五届ACM决赛-重现赛C Captcha Cracker (字符串模拟)
链接:https://ac.nowcoder.com/acm/contest/3/C 来源:牛客网 Captcha Cracker 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 26 ...
- bzoj4810 [Ynoi2017]由乃的玉米田 莫队+bitset(+数论)
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4810 题解 看数据范围和题目名字应该是根号算法. 因为询问除了区间外,还有第 \(3\) 个参 ...
- 前端每日实战:16# 视频演示如何用纯 CSS 创作一个渐变色动画边框
效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/odpRKX 可交互视频教程 此视频 ...