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
大部分东西上面两篇博客已经讲的很清楚了,这里补充一点自己的学习时候没清楚的点
public boolean offer(E e) { checkNotNull(e); final Node<E> newNode = new Node<E>(e); for (Node<E> t = tail, p = t;;) { Node<E> q = p.next; if (q == null) { if (p.casNext(null, newNode)) { if (p != t) casTail(t, newNode); return true; }//1CAS自旋失败 } else if (p == q)//2某个线程提前出队节点导致p=q,详情见第一篇参考贴 p = (t != (t = tail)) ? t : head; else//3让p指针始终指向尾节点 p = (p != t && t != (t = tail)) ? t : q; } }
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,测试代码如下
class No { int k, v; public No(int k, int v) { this.k = k; this.v = v; } } No B = new No(3, 4); No A = B;//当A=B时返回1 2 //No A = new No(1, 9);//当A!=B时返回3 4 No C = new No(1, 2); No D = (A!=(A=B))?A:C; System.out.println(D.k + " " + D.v);
然后还有一句这个,p = (p != t && t != (t = tail)) ? t : q;
根据&&的特性,如果p=t那么&&后的语句将不会被执行,也就是说t最后没有被赋值
3、可以发现程序当前已经经过casNext方法,可以发现此时p指针并没有指向新节点newNode