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 他解释的很好很好,主要是它这个变量名起的好,一个好的变量名是多么的重要。


[img]http://dl2.iteye.com/upload/attachment/0113/3726/a7a1040f-4f00-33d1-b71f-a2c4426fd1fb.png[/img]


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

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

我翻译一遍我对上面程序的理解:
【步骤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 这个博客 主要是那几个图。
或者看并发编程实践 这本书。
对同一个知识点 多几个方面去看,说不定啥时候一下子就通了……
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值