一、背景
之前写过一篇《ConCurrentHashMap 源码解析》。
一读者,加了我微信,前几天发信息说:
解析不太对吧,源码好像有问题!
addCount
方法中的错误如下:
起初我内心,是相当抗拒的,
JDK8 源码能写错?闹着玩么?
常用并发工具类啊,怎么可能有错!
硬着头皮,看了大半天,
好像是不对哦!问问度娘,查查官网,
我去!!!JDK8 的源码,确实写的不对!!!
二、结论
我查到了 Oracle 官网上,此 bug 的说明——“ConcurrentHashMap.addCount()”
为什么错了? 为什么?
if (sc < 0) {
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
对于 sc == rs + 1
来说,等号左边是 负数,
而等号右边是 正数,这个判断永远是 false
;
稍微改下就对了: (sc >>> RESIZE_STAMP_SHIFT) == rs + 1
。
好了,对于面试来讲,你能扯出一个 JDK 的 bug,
很拽!很牛气!这就行了。
下面的源码解析,很枯燥,也很烧脑。
压根没看过源码的,还是别往下看了。
`
-------------- !!!劝退分割线!!!--------------
`
三、bug 的影响分析
这个 bug 对 ConCurrentHashMap
有啥大的影响么?
大的影响,没有!小的影响,也可以认为没有。
你想啊,这是 JDK8
常用并发工具类,真有问题,早就修复了。
这个问题,到 JDK12
才更正了,也能侧面看出来,它无足轻重!
如果非要说有啥影响,在极端高并发时,某些线程会多执行几行代码,
0.001 毫秒为一个单位,耗时会多几个单位吧。
为什么,这个后面说道源码再细说。
网上其实也有文章,说这个 bug 的,
像我这样,解释详细的文章,就几乎没有了。
(sc >>> RESIZE_STAMP_SHIFT) == rs + 1
这个为什么是对的,可能这是全网独一份的解释。
自夸了,哈哈!!
四、sc == rs + 1 判断无效,源码解析
《addCount 源码解析》,你先看下,这篇文章,
对相关源码有个详细的了解。
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n