震惊!ConcurrentHashMap里面也有死循环,作者留下的“彩蛋”了解一下? (2)

简介: 震惊!ConcurrentHashMap里面也有死循环,作者留下的“彩蛋”了解一下? (2)

走起:


通过前面的分析我们知道了,当前案例情况下,只会进入 1672 行这个分支。

而这个分支里面,还有四个判断。我们一个个的攻破:


标号为 ⑤ 的地方,tabAt 方法取出来的对象,就是之前 “AaAa” 放进去的占位的 ReservationNode ,也就是这个 f 。所以可以进入这个分支判断。


标号为 ⑥ 的地方,fh >=0 。而 fh 是当前 node 的 hash 值,大于 0 说明当前是按照链表存储的数据。之前我们分析过了,当前的 hash 值是 -3。所以,不会进入这个分支。


标号为 ⑦ 的地方,判断 f 节点是否是红黑树存储。当然不是的。所以,不会进入这个分支。


标号为 ⑧ 的地方,binCount 代表的是该下标里面,有几个 node 节点。很明显,现在一个都没有。所以当前的 binCount 还是 0 。所以,不会进入这个分支。

完了。分析完了。


Bug 也就出来了,一次 for 循环结束后,没有 break。苦就苦在这个 for 循环还是个死循环。


再来一个上帝视角,看看当 key 为 “BBBB” 的时候发生了什么事情:


进入无限循环内:


①.经过 “AaAa” 之后,tab 就不为 null 了。


②.当前的槽中已经被 “AaAa” 先放了一个 ReservationNode 进行占位了,所以不为 null。


③.当前的 map 并没有进行扩容操作。


④.包含⑤、⑥、⑦、⑧。


⑤.tabAt 方法取出来的对象,就是之前 “AaAa” 放进去的占位的 ReservationNode,所以满足条件进入分支。


⑥.判断当前是否是链表存储,不满足条件,跳过。


⑦.判断当前是否是红黑树存储,不满足条件,跳过。


⑧.判断当前下标里面是否放了 node,不满足条件(“AaAa” 只有个占位的Node ,并没有初始完成,所以还没有放到该下标里面),进入下一次循环。

然后它就在死循环里面出不来了!


我相信现在大家对于这个 Bug 的来路了解清楚了。


如果你是在 idea 里面跑这个测试用例,也可以这样直观的看一眼:


点击这个照相机图标:


从线程快照里面其实也是可以看到端倪的,大家可以去分析分析。


有的观点说的是由于线程安全的导致的死循环,经过分析我觉得这个观点是不对的。


它存在死循环,不是由于线程安全导致的,纯粹是自己进入了死循环。


或者说,这是一个“彩蛋”?


或者......自信点,就说这事 Bug ,能稳定复现的那种。


那么我们如果是使用 JDK 8 怎么避免踩到这个“彩蛋”呢?


看看 Dubbo 里面是怎么解决的:


先调用了 get 方法,如果返回为 null,则调用 putIfAbsent 方法,这样就能实现和之前一样的效果了。


如果你在项目中也有使用 computeIfAbsent 的地方,建议也这样去修改。


说到 ConcurrentHashMap get 方法返回 null,我就想起了之前讨论的一个面试题了:



答案都写在这个文章里面了,有兴趣的可以了解一下《这道面试题我真不知道面试官想要的回答是什么》


Bug 的解决 其实彻底理解了这个 Bug 之后,我们再来看一下 JDK 9 里面的解决方案,看一下官方源码对比:


http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/ConcurrentHashMap.java?r1=1.258&r2=1.259&sortby=date&diff_format=f


就加了两行代码,判断完是否是红黑树节点后,再判断一下是否是 ReservationNode 节点,因为这个节点就是个占位节点。如果是,则抛出异常。


就这么简单。没有什么神秘的。


所以,如果你在 JDK 9 里面执行文本的测试用例,就会抛出 IllegalStateException


这就是 Doug Lea 之前提到的解决方案:


了解了这个 Bug 的来龙去脉后,特别是看到解决方案后,我们就能轻描淡写的说一句:

害,就这?没听说过!


另外,我看 JDK 9 修复的时候还不止修复了一个问题:


http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/6dd59c01f011/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java


你去翻一翻。发现,啊,全是知识点啊,学不动了。

目录
相关文章
|
7月前
|
存储 Java 开发者
面试官:小伙子知道synchronized的优化过程吗?我:嘚吧嘚吧嘚,面试官:出去!
面试官:小伙子知道synchronized的优化过程吗?我:嘚吧嘚吧嘚,面试官:出去!
75 1
|
7月前
|
存储 缓存 安全
面试官:小伙子,能聊明白JMM给你SSP!我:嘚吧嘚吧一万字,直接征服面试官!
面试官:小伙子,能聊明白JMM给你SSP!我:嘚吧嘚吧一万字,直接征服面试官!
52 1
|
存储 缓存 安全
Java并发编程73道面试题及答案 —— 这也太棒了(一)
Java并发编程73道面试题及答案 —— 这也太棒了
|
算法 C++
【每日算法Day 73】学妹大半夜私聊我有空吗,然后竟然做出这种事!
【每日算法Day 73】学妹大半夜私聊我有空吗,然后竟然做出这种事!
|
Java
这篇 ReentrantLock 看不懂,加我我给你发红包(三)
在开始本篇文章的内容讲述前,先来回答我一个问题,为什么 JDK 提供一个 synchronized 关键字之后还要提供一个 Lock 锁,这不是多此一举吗?难道 JDK 设计人员都是沙雕吗?
140 1
这篇 ReentrantLock 看不懂,加我我给你发红包(三)
|
算法 测试技术
算法 | 高楼丢鸡蛋(源自谷歌面试题)| 刷题打卡
算法 | 高楼丢鸡蛋(源自谷歌面试题)| 刷题打卡
163 0
算法 | 高楼丢鸡蛋(源自谷歌面试题)| 刷题打卡
|
Java 调度
这篇 ReentrantLock 看不懂,加我我给你发红包(二)
在开始本篇文章的内容讲述前,先来回答我一个问题,为什么 JDK 提供一个 synchronized 关键字之后还要提供一个 Lock 锁,这不是多此一举吗?难道 JDK 设计人员都是沙雕吗?
102 0
这篇 ReentrantLock 看不懂,加我我给你发红包(二)
|
Java 调度
这篇 ReentrantLock 看不懂,加我我给你发红包(一)
在开始本篇文章的内容讲述前,先来回答我一个问题,为什么 JDK 提供一个 synchronized 关键字之后还要提供一个 Lock 锁,这不是多此一举吗?难道 JDK 设计人员都是沙雕吗?
89 0
这篇 ReentrantLock 看不懂,加我我给你发红包(一)
|
Java 程序员
学妹问我Java异常是怎么回事,讲了半夜才明白,速度收藏!!!记得点赞和关注
异常是Java开发中常见的,也是程序最不愿意看到的,因为有异常基本上就代表我们写的代码有bug,很烦,游戏服务端有异常上报系统,每当半夜收到异常上报都慌的一笔。今天就扒一扒异常,开始走起。
151 0
学妹问我Java异常是怎么回事,讲了半夜才明白,速度收藏!!!记得点赞和关注
深夜!小胖问我,什么是自旋锁?怎么使用?适用场景是啥?
深夜!小胖问我,什么是自旋锁?怎么使用?适用场景是啥?
深夜!小胖问我,什么是自旋锁?怎么使用?适用场景是啥?