ConcurrentLinkedQueue基于JDK1.8源码学习心得

最近在读《Java并发编程的艺术》,书中关于ConcurrentLinkedQueue的描述并不是基于JDK1.8

相较于JDK1.8版本的,还是修改了挺多地方的,比如关于HOPS的设计意图直接去掉了

参考贴:https://www.jianshu.com/p/08e8b0c424c0
https://blog.csdn.net/lejustdoit/article/details/98477819?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

大部分东西上面两篇博客已经讲的很清楚了,这里补充一点自己的学习时候没清楚的点

 1 public boolean offer(E e) {
 2     checkNotNull(e);
 3     final Node<E> newNode = new Node<E>(e);
 4     
 5     for (Node<E> t = tail, p = t;;) {
 6          Node<E> q = p.next;
 7          if (q == null) {
 8              if (p.casNext(null, newNode)) {
 9                  if (p != t)
10                      casTail(t, newNode);
11                  return true;
12              }//1CAS自旋失败
13          }
14          else if (p == q)//2某个线程提前出队节点导致p=q,详情见第一篇参考贴
15              p = (t != (t = tail)) ? t : head;
16          else//3让p指针始终指向尾节点
17              p = (p != t && t != (t = tail)) ? t : q;
18     }
19 }

1、注解1,2,3都是当前节点插入失效,需要重新插入,2考虑了tail滞后于head的情况(节点出队,详情见第一篇参考贴)

注解3和4中的t=tail这步操作很神奇,它有效的减少了CAS更新tail节点的次数,这也是曾经的HOPS的设计意图

t=tail跟CAS相比有线程安全问题,但是这里对线程安全的要求并不高,因为判断是否真的是尾节点在if (q == null),所以它只是象征性的更新一下尾节点

若刚好更新成为尾节点,那么CAS更新casTail(t, newNode)这步不就不用做了

若运气不好没有更新尾节点,那么并不影响下次循环

2、p = (t != (t = tail)) ? t : head;

这里比较的是地址是否相同,而不是值

刚开始以为一直是true,先执行t=tail,然后判断t!=t,debug的时候发现不是

首先判断t的地址是否是tail,然后才是t=taill,测试代码如下

 1 class No {
 2     int k, v;
 3     public No(int k, int v) {
 4         this.k = k;
 5         this.v = v;
 6     }
 7 }
 8 No B = new No(3, 4);
 9 No A = B;//当A=B时返回1 2
10 //No A = new No(1, 9);//当A!=B时返回3 4
11 No C = new No(1, 2);
12 No D = (A!=(A=B))?A:C;
13 System.out.println(D.k + " " + D.v);

然后还有一句这个,p = (p != t && t != (t = tail)) ? t : q;

根据&&的特性,如果p=t那么&&后的语句将不会被执行,也就是说t最后没有被赋值

3、可以发现程序当前已经经过casNext方法,可以发现此时p指针并没有指向新节点newNode

原文地址:https://www.cnblogs.com/taozi1115402474/p/13192068.html