linkQueue

源地址:http://greemranqq.iteye.com/blog/2216287

推荐:http://ifeve.com/concurrentlinkedqueue/

  http://blog.csdn.net/xin_jmail/article/details/26157971

 

 

源码分析-ConcurrentLinkedQueue

博客分类:

一.序言

     现在并发操作中都要求高效,都在想怎么去掉直接加锁带来的线程切换的开销,这里分享自己对concurrentLinkedQueue  的部分代码的理解,看看他无锁的原因,了解大神的设计思路。

关于 它的工作流程 参考JDK1.6 :http://ifeve.com/concurrentlinkedqueue/ 

本文分析基于JDK 1.7.0_79

 

二.源码分析

    1.介绍:concurrentlinkedqueue 设计有head  和 tail 两个节点,以及节点类 Node,主要看Node 部分

      

  

Java代码  收藏代码
  1. private static class Node<E> {  
  2.        // Node 里面包含了 item 节点值  以及 下一个节点 next  
  3.        // item 和 next 都是valatile  可见性保证了  
  4.        volatile E item;  
  5.        volatile Node<E> next;  
  6.   
  7.        private static final sun.misc.Unsafe UNSAFE;  
  8.        // 并且初始化的时候 就会获得item 和 next 的偏移量  
  9.        // 这为后面的cas 做了准备,如何使用继续看下面  
  10.        private static final long itemOffset;  
  11.        private static final long nextOffset;  
  12.        static {  
  13.            try {  
  14.                UNSAFE = sun.misc.Unsafe.getUnsafe();  
  15.                Class k = Node.class;  
  16.                itemOffset = UNSAFE.objectFieldOffset  
  17.                    (k.getDeclaredField("item"));  
  18.                nextOffset = UNSAFE.objectFieldOffset  
  19.                    (k.getDeclaredField("next"));  
  20.            } catch (Exception e) {  
  21.                throw new Error(e);  
  22.            }  
  23.        }  
  24.    }  

    

 

   2.从常规的 offer 方法进入,因为并发的主要也是offer 和 remove 上的竞争。

     

Java代码  收藏代码
  1. public boolean offer(E e) {  
  2.         checkNotNull(e);// 检查,为空直接异常  
  3.         // 创建新节点,并将e 作为节点的item  
  4.         final Node<E> newNode = new Node<E>(e);  
  5.         // 这里操作比较多,将尾节点tail 赋给变量 t,p  
  6.         for (Node<E> t = tail, p = t;;) {  
  7.             // 并获取q 也就是 tail 的下一个节点  
  8.             Node<E> q = p.next;  
  9.             // 如果下一个节点是null,说明tail 是处于尾节点上  
  10.             if (q == null) {  
  11.                 // 然后用cas 将下一个节点设置成为新节点  
  12.                 // 这里用cas 操作,如果多线程的情况,总会有一个先执行成功,失败的线程继续执行循环。  
  13.                 // 关于casNext 的分析,跳转 1.1  
  14.                 // <A>   
  15.                 if (p.casNext(null, newNode)) {  
  16.                     // 如果p.casNext有个线程成功了,p=newNode   
  17.                     // 比较 t (tail) 是不是 最后一个节点  
  18.                     if (p != t)   
  19.                         // 如果不等,就利用cas将,尾节点移到最后  
  20.                         // 如果失败了,那么说明有其他线程已经把tail移动过,也是OK的  
  21.                         <B>  
  22.                         casTail(t, newNode);    
  23.                     return true;  
  24.                 }  
  25.                 // 如果<A>失败了,说明肯定有个线程成功了,  
  26.                 // 这时候失败的线程,又会执行for 循环,再次设值,直到成功。  
  27.             }  
  28.             else if (p == q)   
  29.                 // 有可能刚好插入一个,然后P 就被删除了,那么 p==q  
  30.                 // 这时候在头结点需要从新定位。  
  31.                 p = (t != (t = tail)) ? t : head;  
  32.             else  
  33.                 // 这里是为了当P不是尾节点的时候,将P 移到尾节点,方便下一次插入  
  34.                 // 也就是一直保持向前推进  
  35.                 p = (p != t && t != (t = tail)) ? t : q;  
  36.         }  
  37.     }  

  

 

   1.1 casNext 分析

    

Java代码  收藏代码
  1. // 对应上面的 Node<E> q = p.next;p.casNext(null,newNode)  
  2. // 他是一个Node 内的方法,  
  3. boolean casNext(Node<E> cmp, Node<E> val) {  
  4.             // 可以看到,它是用p.next (null) 未偏移量,设置新值的  
  5.             // cas 是可以根据内存中的偏移量 改变值,详细这里不解释  
  6.             return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);  
  7. }  
  8. // 既然是可以并发执行,那么当多个线程同一时间执行到这里的时候,必然只有1个 成功,后面的都失// 败。关于成功和失败的处理 继续回到上面 1   

   

    3.poll 方法解释

    

Java代码  收藏代码
  1. public E poll() {  
  2.         // 设置起始点  
  3.         restartFromHead:  
  4.         for (;;) {  
  5.             for (Node<E> h = head, p = h, q;;) {  
  6.                 E item = p.item;  
  7.                 // 利用cas 将第一个节点,设置未null  
  8.                 if (item != null && p.casItem(item, null)) {  
  9.                     // 和上面类似,p的next被删了,  
  10.                     // 然后然后判断一下,目的为了保证head的next不为空  
  11.                     if (p != h) // hop two nodes at a time  
  12.                         updateHead(h, ((q = p.next) != null) ? q : p);  
  13.                     return item;  
  14.                 }  
  15.                 else if ((q = p.next) == null) {  
  16.                     // 有可能已经被另外线程先删除了下一个节点  
  17.                     // 那么需要先设定head 的位置,并返回null  
  18.                     updateHead(h, p);  
  19.                     return null;  
  20.                 }  
  21.                 else if (p == q)  
  22.                     // 这个一般是删完了(有点模糊)  
  23.                     continue restartFromHead;  
  24.                 else  
  25.                     // 和offer 类似,这历使用保证下一个节点有值,才能删除  
  26.                     p = q;  
  27.             }  
  28.         }  
  29.     }  

 

 

   4.remove 方法

    

Java代码  收藏代码
  1. public boolean remove(Object o) {  
  2.         if (o == null) return false;  
  3.         Node<E> pred = null;  
  4.         // 这里是从head 开始的,中间还涉及到head 的判断等从操作  
  5.         // 跟一般for 循环类似,要遍历的- -,这样的操作o 很靠后的时候,会慢- -!  
  6.         // - -不太喜欢这方法,了解作用  
  7.         for (Node<E> p = first(); p != null; p = succ(p)) {  
  8.             E item = p.item;  
  9.             if (item != null &&  
  10.                     o.equals(item) &&  
  11.                     p.casItem(item, null)) {  
  12.                 Node<E> next = succ(p);  
  13.                 if (pred != null && next != null)  
  14.                     pred.casNext(p, next);  
  15.                 return true;  
  16.             }  
  17.             pred = p;  
  18.         }  
  19.         return false;  
  20.     }  

 

   5.size 分析

    

Java代码  收藏代码
  1. public int size() {  
  2.         int count = 0;  
  3.         // 很纠结的是,这里依然用循环获取,判断节点是否有值。然后累加  
  4.         // 比较伤,可能作者是考虑offer poll 等操作开始计算,难以控制,用这种原始的方法  
  5.         // 比较伤- -,做为了解  
  6.         for (Node<E> p = first(); p != null; p = succ(p))  
  7.             if (p.item != null){  
  8.                 // Collection.size() spec says to max out  
  9.                 if (++count == Integer.MAX_VALUE)  
  10.                     break;  
  11.             }  
  12.         return count;  
  13.     }  

 

   

   6.contains 方法

     

Java代码  收藏代码
  1. // 这个方法 也是用线性 遍历 比较的,也不多说了    
  2. public boolean contains(Object o) {  
  3.         if (o == null) return false;  
  4.         for (Node<E> p = first(); p != null; p = succ(p)) {  
  5.             E item = p.item;  
  6.             if (item != null && o.equals(item))  
  7.                 return true;  
  8.         }  
  9.         return false;  
  10.     }  
posted @ 2017-08-21 21:56  handalao  阅读(285)  评论(0编辑  收藏  举报