ConcurrentLinkedQueue

ConcurrentLinkedQueue 能解决什么问题?什么时候使用 ConcurrentLinkedQueue?

1)ConcurrentLinkedQueue 是基于单向链表实现的线程安全【基于 CAS 实现】的、无界、FIFO、非阻塞队列。
2)ConcurrentLinkedQueue 的 offer 和 poll 操作都是非阻塞的。

如何使用 ConcurrentLinkedQueue?

1)ConcurrentLinkedQueue 并发性能比 LinkedBlockingQueue 高,但是当无元素可用时,
频繁的自旋拉取会导致 CPU 飙升,所以当消费者没有拉取到元素时,建议休眠指定的时间后再重试。

使用 ConcurrentLinkedQueue 有什么风险?

1)由于是无界非阻塞队列,当生产速率持续大于消费速率时,会导致资源耗尽,内存溢出。
2)极高的并发场景下,自旋 CAS 长时间不成功会给 CPU 带来非常大的执行开销。

ConcurrentLinkedQueue 核心操作的实现原理?

创建实例

    static final class Node<E> {
volatile E item;
volatile Node<E> next; /**
* 创建一个持有 item 的新节点
*/
Node(E item) {
ConcurrentLinkedQueue.ITEM.set(this, item);
} /** Constructs a dead dummy node. */
Node() {} // 写入后置节点
void appendRelaxed(Node<E> next) {
ConcurrentLinkedQueue.NEXT.set(this, next);
} // 尝试原子更新 item
boolean casItem(E cmp, E val) {
return ConcurrentLinkedQueue.ITEM.compareAndSet(this, cmp, val);
}
} /**
* 队列的第一个活性节点
* Invariants:
* - all live nodes are reachable from head via succ()
* 所有的节点都可以从 head 开始,通过递归 succ() 到达
* - head != null
* head 节点不为 null
* - (tmp = head).next != tmp || tmp != head
* 不会出现自连接
* Non-invariants:
* - head.item may or may not be null.
* - it is permitted for tail to lag behind head, that is, for tail
* to not be reachable from head!
*/
transient volatile Node<E> head; /**
* A node from which the last node on list (that is, the unique
* node with node.next == null) can be reached in O(1) time.
* Invariants:
* - the last node is always reachable from tail via succ()
* 最后一个节点总是可以从 tail 递归 succ() 方法到达
* - tail != null
* tail 节点不为 null
* Non-invariants:
* - tail.item may or may not be null.
* - 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<E> tail; /**
* 创建一个空的 ConcurrentLinkedQueue 实例
*/
public ConcurrentLinkedQueue() {
head = tail = new Node<>();
}

插入元素

    /**
* 将元素 e 插入到队列尾部,由于是无界队列,该操作不会被阻塞 && 返回值永远是 true
*/
@Override
public boolean offer(E e) {
// 元素值不允许为 null
final Node<E> newNode = new Node<>(Objects.requireNonNull(e));
/**
* t:tail
* p:predecessor
* 1)读取尾节点
*/
for (Node<E> t = tail, p = t;;) {
// 读取尾节点的后置节点
final Node<E> q = p.next;
// 1)当前尾节点为最后一个节点
if (q == null) {
// p is last node
// 则尝试将新节点链接到其后面
if (ConcurrentLinkedQueue.NEXT.compareAndSet(p, null, newNode)) {
/**
* 成功的 CAS 操作使得元素 e 成为队列的节点。
*/
if (p != t) {
// 如果其他线程并发修改了 tail 节点,则尝试更新 tail 节点。
ConcurrentLinkedQueue.TAIL.weakCompareAndSet(this, t, newNode);
}
return true;
}
// Lost CAS race to another thread; re-read next
}
// 2)tail 节点和最后一个节点间隔为 2
else if (p == q) {
/**
* We have fallen off list. If tail is unchanged, it will also be off-list,
* in which case we need to jump to head, from which all live nodes are always reachable.
* Else the new tail is a better bet.
* 1)如果尾节点未改变,则读取头节点
* 2)否则读取新的尾节点
*/
p = t != (t = tail) ? t : head;
// 3)如果滞后了两个节点,则重新读取 tail 节点
} else {
/**
* Check for tail updates after two hops.
* 1)tail 节点和最后一个节点间隔为 2,并且尾部节点已经更新,则读取新的尾部节点
* 2)否则更新 p 为其后置节点 q
*/
p = p != t && t != (t = tail) ? t : q;
}
}
}

拉取元素

    /**
* 移除并返回队列头部元素,如果队列为空,则返回 null
* created by ZXD at 8 Dec 2018 T 17:12:16
* @return
*/
@Override
public E poll() {
restartFromHead: for (;;) {
// 读取头节点
for (Node<E> h = head, p = h, q;; p = q) {
final E item;
// 1)head 节点元素不为 null,则尝试将其更新为 null,此时的 head 不是傀儡节点。
if ((item = p.item) != null && p.casItem(item, null)) {
/**
* Successful CAS is the linearization point for item to be removed from this queue。
* 成功的 CAS 操作说明我们已经移除了队列头部元素
*/
if (p != h) {
// 头节点是哨兵节点,则一次移动两个位置,新头节点为数据节点
updateHead(h, (q = p.next) != null ? q : p);
}
// 返回目标元素
return item;
}
// 后置节点为 null
else if ((q = p.next) == null) {
// 将头结点 h 更新为 p
updateHead(h, p);
return null;
}
else if (p == q) {
continue restartFromHead;
}
}
}
}

ConcurrentLinkedQueue 源码分析的更多相关文章

  1. 死磕 java集合之ConcurrentLinkedQueue源码分析

    问题 (1)ConcurrentLinkedQueue是阻塞队列吗? (2)ConcurrentLinkedQueue如何保证并发安全? (3)ConcurrentLinkedQueue能用于线程池吗 ...

  2. 多线程高并发编程(11) -- 非阻塞队列ConcurrentLinkedQueue源码分析

    一.背景 要实现对队列的安全访问,有两种方式:阻塞算法和非阻塞算法.阻塞算法的实现是使用一把锁(出队和入队同一把锁ArrayBlockingQueue)和两把锁(出队和入队各一把锁LinkedBloc ...

  3. 【JUC】JDK1.8源码分析之ConcurrentLinkedQueue(五)

    一.前言 接着前面的分析,接下来分析ConcurrentLinkedQueue,ConcurerntLinkedQueue一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素 ...

  4. JUC源码分析-集合篇(三)ConcurrentLinkedQueue

    JUC源码分析-集合篇(三)ConcurrentLinkedQueue 在并发编程中,有时候需要使用线程安全的队列.如果要实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法. ...

  5. MyCat源码分析系列之——BufferPool与缓存机制

    更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...

  6. rxjava源码分析

    RXjava响应式编程 此文作者大暴雨原创,转载请注明出处. 如果线程的知识不是很丰富,请先查看     rxjava源码中的线程知识  一文 rxjava总结就是:异步实现主要是通过扩展观察者模式 ...

  7. Tomcat处理HTTP请求源码分析(下)

    转载:http://www.infoq.com/cn/articles/zh-tomcat-http-request-2 很多开源应用服务器都是集成tomcat作为web container的,而且对 ...

  8. 【Zookeeper】源码分析之服务器(四)

    一.前言 前面分析了LeaderZooKeeperServer,接着分析FollowerZooKeeperServer. 二.FollowerZooKeeperServer源码分析 2.1 类的继承关 ...

  9. Mybatis源码分析-BaseExecutor

    根据前文Mybatis源码分析-SqlSessionTemplate的简单分析,对于SqlSession的CURD操作都需要经过Executor接口的update/query方法,本文将分析下Base ...

随机推荐

  1. 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)

    洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...

  2. TypeScript ES6-Promise 递归遍历文件夹中的文件

    貌似很多人都爱用这个作为写文章的初尝试,那来吧.遍历文件夹下的所有文件,如遍历文件夹下并操作HTML/CSS/JS/PNG/JPG步骤如下:1.传入一个路径,读取路径里面所有的文件:2.遍历读取的文件 ...

  3. 结构体指针,C语言结构体指针详解

    结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针. 指向结构体变量的指针 前面我们通过“结构体变量名.成员名”的方式引用结构体变量中的成员,除了这种方法之外还可以使用指针. 前面讲过,& ...

  4. solr集群搭建(SolrCloud)

    SolrCloud(solr 云)是 Solr 提供的分布式搜索方案,当你需要大规模,容错,索引量很大,搜索请求并发很高时可以使用SolrCloud.它是基于 Solr 和Zookeeper的分布式搜 ...

  5. django部署到linux上不显示.svg图标处理方法

    在setting文件的最开始添加如下内容: import mimetypes mimetypes.add_type("image/svg+xml", ".svg" ...

  6. sklearn逻辑回归库函数直接拟合数据

    from sklearn import model_selection from sklearn.linear_model import LogisticRegression from sklearn ...

  7. 牛客练习赛49 B 筱玛爱阅读 (状压DP,子集生成)

    链接:https://ac.nowcoder.com/acm/contest/946/B 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262875K,其他语言5257 ...

  8. 以太坊智能合约开发工具 Truffle 入门1

    Truffle是以太坊(Ethereum)智能合约开发的瑞士军刀,小巧好用,上手简单. 本篇文章主要展示如何用Truffle 开发第一个Ethereum智能合约. 1.准备工作:(本人针对window ...

  9. jenkins git项目clean before checkout 和 wipe out repository & force clone

    clean before checkout:会先执行一遍git clone,删除一些untracked文件和目录,比如删除上一次打包编译产生的文件 wipe out repository & ...

  10. 前端之JQuery:JQuery属性操作

    Jquery2--属性相关的操作 知识点总结 1.属性 属性(如果你的选择器选出了多个对象,那么默认只会返回出第一个属性). attr(属性名|属性值) - 一个参数是获取属性的值,两个参数是设置属性 ...