ConcurrentLinkedQueue

在考虑并发的时候可以先考虑单线程的情况,然后再将并发的情况考虑进来。

比如ConcurrentLinkedQueue:

  1. 先考虑单线的offer
  2. 再考虑多线程时候的offer:
    • 多个线程offer
    • 部分线程offer,部分线程poll
      • offer比poll快
      • poll比offer快

offer

public boolean offer(E e) {
checkNotNull(e);
// 新建一个node
final Node<E> newNode = new Node<E>(e); // 不断重试(for只有初始化条件,没有判断条件),直到将node加入队列
// 初始化p、t都是指向tail
// 循环过程中一直让p指向最后一个节点。让t指向tail
for (Node<E> t = tail, p = t;;) {
// q一直指向p的下一个
Node<E> q = p.next;
if (q == null) {
// p is last node
// 如果q为null表示p是最后一个元素,尝试加入队列
// 如果失败,表示其他线程已经修改了p指向的节点
if (p.casNext(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
// node加入队列之后,tail距离最后一个节点已经相差大于一个了,需要更新tail
if (p != t) // hop two nodes at a time
// 这儿允许设置tail为最新节点的时候失败,因为添加node的时候是根据p.next是不是为null判断的,
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
}
else if (p == q)
// 虽然q是p.next,但是因为是多线程,在offer的同时也在poll,如offer的时候正好p被poll了,那么在poll方法中的updateHead方法会将head指向当前的q,而把p.next指向自己,即:p.next == p
// 这个时候就会造成tail在head的前面,需要重新设置p
// 如果tail已经改变,将p指向tail,但这个时候tail依然可能在head前面
// 如果tail没有改变,直接将p指向head
// 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.
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
// tail已经不是最后一个节点,将p指向最后一个节点
p = (p != t && t != (t = tail)) ? t : q;
}
}

poll

public E poll() {
// 如果出现p被删除的情况需要从head重新开始
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item; if (item != null && p.casItem(item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
else if ((q = p.next) == null) {
// 队列为空
updateHead(h, p);
return null;
}
else if (p == q)
// 当一个线程在poll的时候,另一个线程已经把当前的p从队列中删除——将p.next = p,p已经被移除不能继续,需要重新开始
continue restartFromHead;
else
p = q;
}
}
} final void updateHead(Node<E> h, Node<E> p) {
if (h != p && casHead(h, p))
h.lazySetNext(h);
}

为什么会出现p == q

假设下面这种情况:

在第一种情况下,线程A执行offer操作:

第一次循环的时候

  1. tail = node0, p = t = node0
  2. 执行 Node<E> q = p.next; 之后q = p.next,也就是node0.next
  3. 执行 if (q == null),不满足,继续往下
  4. 到了 else if (p == q) 这一步的时候线程A暂停

线程A现在的结果是:

head = node0
tail = node0
t = node0
p = node0
q = node0.next

因为程序时多线程的,我们假设线程A暂定在了第4步,接下来看线程B,线程B执行poll操作:

第一次循环:

  1. head = node0, p = h = node0;
  2. 执行 E item = p.item;,item = null
  3. if (item != null && p.casItem(item, null)) 不满足
  4. 执行 else if ((q = p.next) == null),q = node1,条件不满足
  5. 执行 else if (p == q),条件不满足
  6. 执行 p = q;,p = node1

第二次循环:

  1. head = node0, h = node0, p = node1;
  2. 执行 E item = p.item;,item = node2
  3. if (item != null && p.casItem(item, null))item != null 满足,如果CAS操作成功
    1. p != h 成立,调用updateHead
    2. 执行 updateHead(h, ((q = p.next) != null) ? q : p); 之后,q = node2
    3. 在updateHead里面
      1. h != p 成立,如果CAS操作成功(将head设置为node2)
      2. 执行 h.lazySetNext(h);,这个时候 h = node0,这个方法执行完之后,node0.next = node0
  4. 将item返回

这个时候队列就是图中第二种情况,线程A结果为:

head = node2
tail = node0
t = node0
p = node0
q = node0.next = node0

看到结果了吧,这个时候 p = q = node0其实主要原因是在 updateHead方法的 h.lazySetNext(h) 操作里面,将旧的head.next设置成为了自己即 head.next = head。但是要注意:是旧的head

从上面分析的过程要注意:

  1. 多线程执行环境,单线程下一定不会出现这种情况
  2. 注意引用赋值比如 Node<E> q = p.next,q指向的是p.next,虽然目前 p.next = node1,但是当p.next指向变了之后,q也就跟着变了
  3. 再就是阅读源码的时候一定要弄清楚调用的每个方法的作用,这样才能对整个方法有一个准确的理解,比如这里的 h.lazySetNext(h);

参考

http://www.jianshu.com/p/7816c1361439

Java并发编程的艺术

Java 线程 — ConcurrentLinkedQueue的更多相关文章

  1. Java线程并发:知识点

    Java线程并发:知识点   发布:一个对象是使它能够被当前范围之外的代码所引用: 常见形式:将对象的的引用存储到公共静态域:非私有方法中返回引用:发布内部类实例,包含引用.   逃逸:在对象尚未准备 ...

  2. Java线程池的几种实现 及 常见问题讲解

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...

  3. java线程安全之并发Queue

    关闭 原 java线程安全之并发Queue(十三) 2017年11月19日 23:40:23 小彬彬~ 阅读数:12092更多 所属专栏: 线程安全    版权声明:本文为博主原创文章,未经博主允许不 ...

  4. java线程高并发编程

    java线程具体解释及高并发编程庖丁解牛 线程概述: 祖宗: 说起java高并发编程,就不得不提起一位老先生Doug Lea,这位老先生可不得了.看看百度百科对他的评价,一点也不为过: 假设IT的历史 ...

  5. 50道Java线程面试题分析及答案

    下面是Java线程相关的热门面试题,你可以用它来好好准备面试. 1) 什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程 ...

  6. Java线程的概念

    1.      计算机系统 使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行:当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存读写了. 缓 ...

  7. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  8. 细说进程五种状态的生老病死——双胞胎兄弟Java线程

    java线程的五种状态其实要真正高清,只需要明白计算机操作系统中进程的知识,原理都是相同的. 系统根据PCB结构中的状态值控制进程. 单CPU系统中,任一时刻处于执行状态的进程只有一个. 进程的五种状 ...

  9. 【转载】 Java线程面试题 Top 50

    Java线程面试题 Top 50 不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员 的欢迎.大多数待遇丰厚的J ...

随机推荐

  1. 通过.net反射技术实现DataReader转换成Model实体类列表

     public static T ReaderToModel<T>(IDataReader dr) { try {  using (dr) {  if (dr.Read()) {  Typ ...

  2. A very cool thing: Install MYSQL from source without root access on LINUX

    最近由于工作的需要,要在centos上安装MYSQL服务器.作为一名小兵中的小兵,当然是没有root权限的,为了能够使用mysql,只能使用源码安装了(因为binary安装方式似乎需要root acc ...

  3. OpenMP之枚举排序

    // EnumSort.cpp : 定义控制台应用程序的入口点. //枚举排序 /* 枚举排序(Enumeration Sort)是一种最简单的排序算法,通常也称为秩排序(Rank Sort). 该算 ...

  4. 萝卜白菜,给有所爱——C#和JAVA都会终将被时代淘汰

    看到园子里又有一波试图掀起C#和JAVA的谁更好的争论,对于这些一直不断的争论,我觉得实在没有必要,黑格尔的存在即合理,中国的老古语说的萝卜白菜各有所爱,大家争论的再多其实卵用也没用,还不如趁着闲暇时 ...

  5. bootstrap双日历插件实例化

    网站中难免会用到日期选择插件,常见的有jquery的,也有bootstrap的.单日历的就不说了,实例化都比较简单.今天给大家介绍一下bootstrap的双日历插件. http://www.jq22. ...

  6. Win7系统修改hosts文件不能保存的解决方法

    地址:http://jingyan.baidu.com/album/e5c39bf56564a539d7603312.html

  7. 练习2-3:十六进制数字字符串转换为等价整型值,字符串允许包含的数字包括:0~9、a~f、A~F、x、X(C程序设计语言 第2版)

    #include <stdio.h> #include <string.h> #include <math.h> int htoi(char s[]){ unsig ...

  8. RHEL5.8的NFS配置文件

    RHEL5.8的NFS配置文件 路径:/etc/sysconfig/nfs # # Define which protocol versions mountd # will advertise. Th ...

  9. 0003--Weekly Meeting on 10th April and 17th April, 2015

    10th April, 2015 (1) Orthogonal Matching Pursuit, Least Angle Regression, Dictionary Coherence. -> ...

  10. 初学python里的yield send next

    今天看书的时候突然看到这个想起来一直没有怎么使用过send和next试了一下 发现了一个诡异的问题 import math def get_primes(start): while 1 : if is ...