ConcurrentLinkedQueue 手把手教你理解

ConcurrentLinkedQueue 线程安全的 队列分析 CAS 步骤分析:

public boolean offer(E e) {    
        if (e == null) throw new NullPointerException();    
        Node<E> n = new Node<E>(e, null);    
        for (;;) {    
//        	获得列表的 尾节点
            Node<E> t = tail;    
//        	获得列表的 尾节点 的下一个节点
            Node<E> s = t.getNext();   
            if (t == tail) { 					//------------------------------a    
                if (s == null) { 				//------------------------------b    
                    if (t.casNext(s, n)) { 		//------------------------------c    
                        casTail(t, n); 			//------------------------------d    
                        return true;    
                    }
                } else {    
                    casTail(t, s); 				//------------------------------e
                }    
            }    
        }    
    }





/** 
【步骤a】:t==tail是最上层的协调,如果其他线程改变了tail的引用,则说明现在获得不是最新的尾指针需要重新循环获得最新的值。 

【步骤b】:s==null的判断。【【【静止状态下tail的next一定是指向null的】】】,但是 多线程下的另一个状态就是中间态:tail的指向没有改变,但是其next已经指向新的结点,即完成tail引用改变前的状态,这时候s!=null。这里就是协调的典型应用,直接进入代码e去协调参与中间态的线程去完成最后的更新,然后重新循环获得新的tail开始自己的新一次的入队尝试。另外值得注意的是a,b之间,其他的线程可能会改变tail的指向,使得协调的操作失败。
从这个步骤可以看到无锁实现的复杂性。
  	
【步骤c】:t.casNext(s, n)是入队的第一步,因为入队需要两步:更新Node的next,改变tail的指向。代码c之前可能发生tail引用指向的改变或者进入更新的中间态,这两种情况均会使得t指向的元素的next属性被原子的改变,不再指向null。这时代码c操作失败,重新进入循环。 

【步骤d】:这是完成更新的最后一步了,就是更新tail的指向,最有意思的协调在这儿又有了体现。从代码看casTail(t, n)不管是否成功都会接着返回true标志着更新的成功。首先如果成功则表明本线程完成了两步的更新,返回true是理所当然的;如果 casTail(t, n)不成功呢?  要清楚的是完成代码c则代表着更新进入了中间态,代码d不成功则是tail的指向被其他线程改变。意味着对于其他的线程而言:它们得到的是中间态的更新,s!=null,进入代码e帮助本线程执行最后一步并且先于本线程成功。这样本线程虽然代码d失败了,但是是由于别的线程的协助先完成了,所以返回true也就理所当然了。 
*/




以上都是摘自博客:http://yanxuxin.iteye.com/blog/586943
看了2、3遍实在是理解不了。
最后看到并发编程实战这本书,P272--P274 他解释的很好很好,主要是它这个变量名起的好,一个好的变量名是多么的重要。







理解完成之后再回来看上面那片博客解释的也不错。
================================以下为个人原创理解部分=========================
先普及几个知识点:
对于一个链表:在稳定状态 也就是没有线程对他进行任何操作的时候,
1、head指向 首节点
2、tail指向 尾节点
3、tail节点的next是指向nul的。  【这个非常重要需要记下了,稳定状态的list它的tail节点的next节点指向null。  tail.getNext() == null表示此链表处于稳点状态 】

链表的入队操作包括两步:
第一步:把当前链表的最后一个元素的 next节点连接到新插入的节点。(本来这个节点是指向null的)
第二步:更新整个链表 tail节点指向刚插入节点。


我翻译一遍我对上面程序的理解:
   【步骤a】:判断刚刚获取到的tail是否有变化,如果有变化重新获取,如果没变化继续往下。因为是多线程操作,即便是上一步刚刚赋值:Node<E> t = tail;     在进行判断t == tail也不一定是一样的。

   【步骤b】:接着上一步,tail节点还没有变化,继续判断链表是否处于稳定状态就是刚才第三点普及的知识。tail.getNext() == null。   如果成立表示,此链表处于稳定状态我可以开始对他进行 入队列操作。进入步骤c                   如果不成立,就表示有线程入队完成了第一步,还没有来得及完成第二步。不成立跳转到步骤e
   【步骤c】:t.casNext(s, n)是入队的第一步。第一步成功,继续下一步更新tail指向新插入的节点。         第一步失败,重新进入循环开始新一轮CAS操作。
   【步骤d】:入队列第一步成功,执行第二步。casTail(t, n);把tail节点指向new节点。这一步有一个特别需要注意的是,不管这一步是否成功。都会返回ture,这不科学啊???骚年无躁,请看步骤b跳转到的步骤e完成了 casTail(t, n); 这一步。
   【步骤e】:casTail(t, s);  更新tail节点到tail.getNext()节点也就是new节点,因为能进入步骤e就说明队列处于不稳定状态。   步骤d 和步骤e 效果其实是一样的。步骤d是无并发情况下正常执行,步骤e是多线程下 第一个线程完成了入队的第一步,第二个线程来完成了入队的第二步。


解释的有点啰嗦……


看不懂的还是多看几遍http://hellosure.iteye.com/blog/1126541 这个博客 主要是那几个图。
或者看并发编程实践 这本书。
对同一个知识点 多几个方面去看,说不定啥时候一下子就通了……

猜你喜欢

转载自ican.iteye.com/blog/2259773