并发编程之-ConcurrentLinkedQueue-源码剖析
Demo
该类继承结构如下:
该类是 Collection 框架下的实现。也就是Java 类库提供的数据结构。
add 方法将指定元素插入此队列的尾部。
poll 方法 获取并移除此队列的头,如果此队列为空,则返回 null。
peek 方法 获取但不移除此队列的头;如果此队列为空,则返回 null。
那么我们就看看 doug lea 是如何实现并发安全的吧。在这之前,我们可以试想一下,实现并发安全无非两种方式,一种是锁,就像我们之前分析的容器,比如 concurrentHashMap,CopyOnWriteArrayList , LinkedBolckingQueue,还有一种是 CAS,在这些容器里也用到了。那么,如果是我们来实现这个队列,使用什么方式呢?有趣的问题。
开始看源码吧。
add 方法源码剖析
实际上是调用 offer 方法,add 方法是 Collection 接口规定的容器方法,而 offer 方法是 Queue 接口的方法。
那我们就看看 offer 方法:
1 | public boolean offer(E e) { |
代码行数很少,楼主注释也写了,这里可以看到 doug lea 使用了 CAS 的方式防止并发错误,同时,也看得出对 tail 变量被修改的担忧,通过 t != t 的判断,来检查 tail 是否被其他线程修改了,而这个offer 操作,如果不成功,则永远不会返回,这个队列同时也是无界的。这点在使用的时候需要注意一下。
那么 poll 方法如何实现呢?
poll 方法源码剖析
1 | public E poll() { |
上面楼主已经写了注释,但是有一个非常困扰哦楼主的疑点,就是 else if (p == q) 这行代码,楼主分析的没有问题,但是再楼主的单线程测试这段代码时,出现了诡异的情况,无法解释,因此, 楼主贴出测试用例,大家一起看看:
测试代码:
断点代码:
注意,断点位置一定要和我的一致。会出现一些奇怪的效果。楼主无法解释,因为这个问题,楼主一直都不敢发这篇文章出来,但楼主觉得有必要说出这个问题,抛砖引玉。
问题在于:单线程怎么会进入这段代码?按道理,但线程是不会出现这个情况的。
总结
这次的源码分析让楼主很痛苦,网上很多的文章也无法解释这是为什么,希望有高人能告诉楼主,到底是怎么回事?