LinkedTransferQueue 源码分析
LinkedTransferQueue
LinkedTransferQueue 能解决什么问题?什么时候使用 LinkedTransferQueue?
1)LinkedTransferQueue 是基于单向链表实现的线程安全的、无界、FIFO、阻塞队列
2)执行 put 操作时,如果链表头部为阻塞的 take 线程,则直接将此元素交给它,
否则将元素添加到队列尾部,put 操作不会阻塞调用线程。
3)执行 transfer 操作的线程需要一个配对的 take 线程读取元素,否则将阻塞,反之也一样。
如何使用 LinkedTransferQueue?
1)LinkedTransferQueue 相对于 LinkedBlockingQueue,可以直接投递元素,性能更高。
2)LinkedTransferQueue 相对于 SynchronousQueue,多了一个可以存储元素的缓冲队列
使用 LinkedTransferQueue 有什么风险?
1)LinkedTransferQueue 是通过无锁 CAS 保证线程安全的,高并发场景下,频繁的 CAS 失败会导致高 CPU 使用率。
2)LinkedTransferQueue 是无界的,生产速率持续大于消费速率时,一定时间后会导致内存溢出。
LinkedTransferQueue 核心操作的实现原理?
- 创建实例
/**
* 头结点
* can be reached in O(1) time.
* Invariants:
* - all live nodes are reachable from head via .next
* - head != null
* - (tmp = head).next != tmp || tmp != head
* Non-invariants:
* - head may or may not be live
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
*/
transient volatile Node head;
/**
* 尾节点
* can be reached in O(1) time.
* Invariants:
* - the last node is always reachable from tail via .next
* - tail != null
* Non-invariants:
* - tail may or may not be live
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
* - tail.next may or may not be self-linked.
*/
private transient volatile Node tail;
/**
* 创建一个空的 LinkedTransferQueue 实例
*/
public LinkedTransferQueue() {
// 傀儡节点的 isData=true
head = tail = new Node();
}
- 异步添加元素
static final class Node {
/**
* 1)true:当前节点是数据节点
* 2)false:当前节点是请求节点
*/
final boolean isData;
/**
* 1)如果是数据节点,新建时为目标元素,匹配时置为 null
* 2)如果是请求节点,新建时为 null,匹配时置为读取的目标元素
*/
volatile Object item; // initially non-null if isData; CASed to match
// 链表的后置节点
volatile Node next;
// 节点的驻留线程,非阻塞数据节点的 waiter 为 null
volatile Thread waiter;
}
/**
* 将目标元素插入到队列尾部,由于队列是无界的,该方法不会阻塞
*/
@Override
public void put(E e) {
xfer(e, true, LinkedTransferQueue.ASYNC, 0);
}
// 实时模式
private static final int NOW = 0; // for untimed poll, tryTransfer
// 异步模式
private static final int ASYNC = 1; // for offer, put, add
// 同步模式
private static final int SYNC = 2; // for transfer, take
// 超时模式
private static final int TIMED = 3; // for timed poll, tryTransfer
/**
* 聚合了所有队列操作
*
* @param e 1)如果是 put 操作,则为插入的目标元素
* 2)如果是 take 操作,则为 null
* @param haveData put 操作时为 true,take 操作为 false
* @param how NOW, ASYNC, SYNC, or TIMED
* @param nanos timeout in nanosecs, used only if mode is TIMED
* @return an item if matched, else e
* @throws NullPointerException if haveData mode but e is null
*/
@SuppressWarnings("unchecked")
private E xfer(E e, boolean haveData, int how, long nanos) {
if (haveData && e == null) {
throw new NullPointerException();
}
restart: for (Node s = null, t = null, h = null;;) {
/**
* t:尾节点
* h:头结点
* p:前置节点
* 1)尾部节点已经被其他线程更新 && 和当前节点非对偶节点,则读取新的尾部节点
* 2)未发生竞争,则读取头部节点
*/
for (Node p = t != (t = tail) && t.isData == haveData ? t
: (h = head);; ) {
final Node q; final Object item;
/**
* 1)前置节点和新值节点是对偶节点 && 前置节点是一个 take 操作
*/
if (p.isData != haveData
&& haveData == ((item = p.item) == null)) {
if (h == null) {
h = head;
}
/**
* 1)尝试将 take 节点的 item 从 null 变为 e,同时唤醒驻留其上的线程
* 操作成功返回 true。
*/
if (p.tryMatch(item, e)) {
// 前置节点不是头结点
if (h != p) {
// 移除队列前端已经匹配的节点
skipDeadNodesNearHead(h, p);
}
return (E) item;
}
}
// 前置节点的后置节点为 null,即 p 是尾节点
if ((q = p.next) == null) {
// 如果是实时操作,则立即返回
if (how == LinkedTransferQueue.NOW) {
return e;
}
// 创建新节点
if (s == null) {
s = new Node(e);
}
// 将新节点链接到尾部,发生竞争则重试
if (!p.casNext(null, s)) {
continue;
}
if (p != t) {
// 将新节点设置为尾部
casTail(t, s);
}
// 如果是异步添加,则直接返回
if (how == LinkedTransferQueue.ASYNC) {
return e;
}
// 超时或阻塞等待对偶节点
return awaitMatch(s, p, e, how == LinkedTransferQueue.TIMED, nanos);
}
// 出现竞争,则重试
if (p == (p = q)) {
continue restart;
}
}
}
}
/**
* 清除 h 到 p 之间的所有匹配节点,包括 h 和 p,理论上它们都应该是已匹配节点
*/
private void skipDeadNodesNearHead(Node h, Node p) {
for (;;) {
final Node q;
// 1)p 节点是尾节点,则直接退出,清除后队列为空
if ((q = p.next) == null) {
break;
// 2)p 节点的后置节点未匹配,则退出
} else if (!q.isMatched()) { p = q; break; }
else if (p == (p = q)) {
return;
}
}
// 更新头结点
if (casHead(h, p)) {
// 释放 head
h.selfLink();
}
}
/**
* Spins/yields/blocks until node s is matched or caller gives up.
*
* @param s 阻塞等待的节点
* @param pred s 的前置节点,
* or null if unknown (the null case does not occur in any current calls but may in possible future extensions)
* @param e 检查匹配的比较值
* @param timed 是否是超时模式
* @param nanos 以纳秒为单位的超时时间
* @return matched item, or e if unmatched on interrupt or timeout
*/
private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) {
// 计算截止时间
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 读取当前线程
final Thread w = Thread.currentThread();
int spins = -1; // initialized after first item and cancel checks
ThreadLocalRandom randomYields = null; // bound if needed
for (;;) {
final Object item;
// 1)尝试阻塞等待过程中,已经出现匹配节点
if ((item = s.item) != e) { // matched
/**
* 将请求节点的 item 设置为节点本身
* 将 waiter 置为 null
*/
s.forgetContents(); // avoid garbage
@SuppressWarnings("unchecked")
final E itemE = (E) item;
// 返回匹配的数据
return itemE;
}
// 2)当前线程被中断或超时
else if (w.isInterrupted() || timed && nanos <= 0L) {
// try to cancel and unlink
if (s.casItem(e, s.isData ? null : s)) {
unsplice(pred, s);
return e;
}
// return normally if lost CAS
}
// 3)自旋次数位初始化,则初始化自旋次数
else if (spins < 0) { // establish spins at/near front
if ((spins = LinkedTransferQueue.spinsFor(pred, s.isData)) > 0) {
randomYields = ThreadLocalRandom.current();
}
}
// 4)自旋还未结束,则递减自旋次数
else if (spins > 0) { // spin
--spins;
// 1/64 的几率出现 Thread.yield()
if (randomYields.nextInt(LinkedTransferQueue.CHAINED_SPINS) == 0)
{
Thread.yield(); // occasionally yield
}
}
// 5)当前节点还无驻留线程,则将当前线程驻留其上,准备进入阻塞
else if (s.waiter == null) {
s.waiter = w; // request unpark then recheck
}
// 6)如果是超时阻塞
else if (timed) {
// 计算剩余时间
nanos = deadline - System.nanoTime();
if (nanos > 0L) {
// 超时阻塞
LockSupport.parkNanos(this, nanos);
}
}
// 7)阻塞当前线程
else {
LockSupport.park(this);
}
}
}
- 同步添加元素
/**
* 1)如果当前有线程阻塞在 take 操作上,则将元素 e 传递给消费者
* 2)否则阻塞等待消费者出现
* 如果当前线程被中断,则抛出 InterruptedException 异常
*/
@Override
public void transfer(E e) throws InterruptedException {
if (xfer(e, true, LinkedTransferQueue.SYNC, 0) != null) {
Thread.interrupted(); // failure possible only due to interrupt
throw new InterruptedException();
}
}
/**
* 如果当前有线程阻塞在 take 操作上,则将元素 e 传递给消费者,操作成功返回 true。
* 操作失败,则元素被丢弃,并返回 false。
*/
@Override
public boolean tryTransfer(E e) {
return xfer(e, true, LinkedTransferQueue.NOW, 0) == null;
}
/**
* 1)在指定的超时时间内将目标元素 e 传递给一个消费者,如果传递成功则返回 true。
* 2)如果超时,则元素被丢弃,并返回 false。
* 3)线程被中断则抛出 InterruptedException 异常
*/
@Override
public boolean tryTransfer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (xfer(e, true, LinkedTransferQueue.TIMED, unit.toNanos(timeout)) == null) {
return true;
}
if (!Thread.interrupted()) {
return false;
}
throw new InterruptedException();
}
- 同步读取元素
/**
* 1)如果头节点是一个数据节点,则读取其数据
* 2)否则将当前线程加入队列尾部,阻塞等待读取数据
* 3)如果线程被中断,则抛出 InterruptedException 异常
*/
@Override
public E take() throws InterruptedException {
final E e = xfer(null, false, LinkedTransferQueue.SYNC, 0);
if (e != null) {
return e;
}
Thread.interrupted();
throw new InterruptedException();
}
/**
* 1)如果头节点是一个数据节点,则读取其数据,否则立即返回 null。
*/
@Override
public E poll() {
return xfer(null, false, LinkedTransferQueue.NOW, 0);
}
/**
* 1)尝试在指定的超时时间内读取数据,成功则返回数据值
* 2)如果已经超时,则返回 null
* 3)如果线程被中断,则抛出 InterruptedException 异常
*/
@Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
final E e = xfer(null, false, LinkedTransferQueue.TIMED, unit.toNanos(timeout));
if (e != null || !Thread.interrupted()) {
return e;
}
throw new InterruptedException();
}
LinkedTransferQueue 源码分析的更多相关文章
- 死磕 java集合之LinkedTransferQueue源码分析
问题 (1)LinkedTransferQueue是什么东东? (2)LinkedTransferQueue是怎么实现阻塞队列的? (3)LinkedTransferQueue是怎么控制并发安全的? ...
- 死磕 java集合之ArrayDeque源码分析
问题 (1)什么是双端队列? (2)ArrayDeque是怎么实现双端队列的? (3)ArrayDeque是线程安全的吗? (4)ArrayDeque是有界的吗? 简介 双端队列是一种特殊的队列,它的 ...
- JUC源码分析-集合篇(十)LinkedTransferQueue
JUC源码分析-集合篇(十)LinkedTransferQueue LinkedTransferQueue(LTQ) 相比 BlockingQueue 更进一步,生产者会一直阻塞直到所添加到队列的元素 ...
- lesson2:java阻塞队列的demo及源码分析
本文向大家展示了java阻塞队列的使用场景.源码分析及特定场景下的使用方式.java的阻塞队列是jdk1.5之后在并发包中提供的一组队列,主要的使用场景是在需要使用生产者消费者模式时,用户不必再通过多 ...
- JDK源码分析(11)之 BlockingQueue 相关
本文将主要结合源码对 JDK 中的阻塞队列进行分析,并比较其各自的特点: 一.BlockingQueue 概述 说到阻塞队列想到的第一个应用场景可能就是生产者消费者模式了,如图所示: 根据上图所示,明 ...
- 细说并发5:Java 阻塞队列源码分析(下)
上一篇 细说并发4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...
- 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 ...
随机推荐
- 列表、元组和range
小知识点 s = " 5 " print(int(s)) print(s.replace(" ","")) 结果: 5 5 id()#获取对 ...
- python 元组及操作
# 元组是'不可变'的 list 使用小括号 创建后不允许修改 # 创建# t = ('a','b','c',1,2,3)# print(t)# print(type(t))# # 3# print( ...
- [LeetCode] 109. 有序链表转换二叉搜索树
题目链接 : https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree/ 题目描述: 给定一个单链表,其中的 ...
- vmware下使用nat方法联网
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/Li_Zefeng/article/det ...
- 剑指offer-栈的压入、弹出序列-栈和队列-python
题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压 ...
- 2014-04-27 南江滨大道 6KM 晴
33分41秒,6.03公里,慢速跑,中间有停了几次拍照 天气不错,多云 人,不多 不知道这货叫啥 2个大人3个小孩,跳绳,小时候的回忆,啊哈 老中少三代,捡风筝也是一种幸福 一家三口,江滨散步,惬意至 ...
- TCP软件环境测试
利用合宙官网上的云平台->TCP透传云,建立一个TCP服务. http://tcplab.openluat.com/ [注意事项] 如3分钟内没有客户端接入则会自动关闭. 每个服务器最大客户端连 ...
- CRMEasy知识库访问权限
知识库设置BCCBINFO文件夹为共享文件夹,给予普通域用户和管理员域用户完全控制权限,但是当以管理员域用户身份对文件进行操作后,以普通域用户登录不会有效果,甚至出现“访问权限”的错误,这是由于文件夹 ...
- [转]tKC(The Keyboard Caper)的自传
每个玩儿计算机的朋友都知道破解,或多或少地使用着各种各样的破解,也就逐渐地知道了一些著名的破解团体和破解人,比如:PC(Phrozen Crew).CiA(Crackers in Action)COR ...
- 【学习】021 Nginx
nginx入门 什么是nginx? nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器.由俄罗斯的程序设计师Igor Sysoev所开发,官方测试ngi ...