大家好,今天小编来为大家解答哪个网站的源码分享好写这个问题,比较好的源码网站很多人还不知道,现在让我们一起来看看吧!
你好呀,我是歪歪。
这次给你盘一个特别有意思的源码,正如我标题说的那样:看懂这行源码之后,我不禁鼓起掌来,直呼祖师爷牛逼。
这行源码是这样的:
java.util.concurrent.LinkedBlockingQueuenextNode
针对if判断中的s==p,我们把s替换一下,就变成了p.next=p:
那么什么时候会出现p.next=p这样的代码呢?
答案就藏在这个方法的注释部分:dequeuednodes(p.next==p)
dequeue这不是巧了吗,这不是和前面给呼应起来了吗?
好,到这里,我要开始给你画图说明了,假设我们LinkedBlockingQueue里面放的元素是这样的:
画图出来就是这样的:
现在我们要对这个链表进行迭代,对应到画图就是这样的:
linkedBlockingQueue.iterator();
看到这个图的时候,问题就来了:current指针是什么时候冒出来的呢?
current,这个变量是在生成迭代器的时候就初始化好了的,指向的是head.next:
然后current是通过nextNode这个方法进行维护的:
正常迭代下,每调用一次都会返回s,而s又是p.next,即下一个节点:
所以,每次调用之后current都会移动一格:
这种情况,完全就没有这个分支的事儿:
什么时候才会和它扯上关系呢?
你想象一个场景。
A线程刚刚要对这个队列进行迭代,而B线程同时在对这个队列进行remove。
对于A线程,刚刚开始迭代,画图是这样的:
然后current还没开始移动呢,B线程“咔咔”几下,直接就把1,2,3全部给干出队列了,于是站在B线程的视角,队列是这样的了:
到这里,你先思考一个问题:1,2,3这几个节点,不管是自己指向自己,还是指向一个null,此时发生一个YGC它们还在不在?
2和3指定是没了,但是1可不能被回收了啊。
因为虽然元素为1的节点出队了,但是站在A线程的视角,它还持有一个current引用呢,它还是“可达”的。
所以,这个时候A线程开始迭代,虽然1被B出队了,但是它一样会被输出。
然后,我们再来对于下面这两种情况,A线程会如何进行迭代:
当1节点的next指为null的时候,即p.next为null,那么满足s==null的判断,所以nextNode方法就会返回s,也就是返回了null:
当你调用hasNext方法判断是否还有下一节点的时候,就会返回false,循环就结束了:
然后,我们站在上帝视角是知道的,后面还有4和5没输出呢,所以这样就会出现问题。
但是,当1节点的next指向自己的时候,有趣的事情就来了:
current指针就变成了head.next。
而你看看当前的这个链表里面head.next是啥?
不就是4节点吗?
这不就衔接上了吗?
所以最终A线程会输出1,4,5。
虽然我们知道1元素其实已经出队了,但是A线程开始迭代的时候,它至少还在。
这玩意就体现了前面提到的:weaklyconsistentiterator,弱一致性迭代器。
这个时候,你再结合者迭代器上的注解去看,就能搞得明明白白了:
如果hasNext方法返回为true,那么就必须要有下一个节点。即使这个节点被比如take等等的方法给移除了,也需要返回它。这就是weakly-consistentiterator。
然后,你再看看整个类开始部分的Javadoc,其实我整篇文章就是对于这一段描述的翻译和扩充:
看完并理解我这篇文章之后,你再去看这部分的Javadoc,你就知道它是在说个啥事情,以及它为什么要这样的去做这件事情了。
好了,看到这里,你现在应该明白了,为什么必须要有h.next=h,为什么不能是h.next=null了吧?
明白了就好。
因为本文就到这里就要结束了。
如果你还没明白,不要怀疑自己,大胆的说出来:什么玩意?写的弯弯绕绕的,看求不懂。呸,垃圾作者。
最后,我还想要说的是,关于LBQ这个队列,我之前也写过这篇文章专门说它:《喜提JDK的BUG一枚!多线程的情况下请谨慎使用这个类的stream遍历。》
文章里面也提到了dequeue这个方法:
但是当时我完全没有思考到文本提到的问题,顺着代码就捋过去了。
我觉得看到这部分代码,然后能提出本文中这两个问题的人,才是在带着自己思考深度阅读源码的人。
解决问题不厉害,提出问题才是最屌的,因为当一个问题提出来的时候,它就已经被解决了。
带着质疑的眼光看代码,带着求真的态度去探索,与君共勉之。
好了,本文到此结束,如果可以帮助到大家,还望关注本站哦!
