不一样的内容:死磕JDK8中ConcurrentHashMap.computeIfAbsent 死循环 Bug


背景:

   最近有朋友提到了JDK1.8中的ConcurrentHashMap有可能引起CPU飙升的问题,立马恶补,因为运行的生产环境就是1.8版本的,希望没有采坑。

浏览后,发现网上文章千篇一律,不全面。经过一上午的分析、研究,总结如下,共同进步~~~~~~~~~~~~

之前文章中提到过《JDK1.7中HashMap引起CPU100%的问题》,那么JDK8中的ConcurrentHashMap也不一定是安全的。
官方Bug报告: https://bugs.openjdk.java.net/browse/JDK-8062841
JDK9中变化内容: http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/6dd59c01f011

再次描述下这个bug,运行如下程序

Map<String, Integer> map = new ConcurrentHashMap<>();
map.computeIfAbsent("a", key -> {
    map.put("a", 2);
    return 1;
});

 

JDK1.8中 computeIfAbsent 部分代码

 

 

ReservationNode 在 computeIfAbsent() 方法构建 value 值的时候被用作占位节点。换句话说,computeIfAbsent() 方法会初始化一个 ReservationNode 来占位,它会等待计算完毕后替换当前的占位对象。此时,如果正好赶在 ConcurrentHashMap 达到容量 0.75 的时候进行扩容,由于ConcurrentHashMap 扩容忽略了 ReservationNode 情况。调用put的时候在synchronized (f)没有对ReservationNode处理,所以会出现死循环。因此,可能会导致扩容无法替换占位符,同时占位符等待替换的情况,然后就一直 for 循环处理了。

这也是为什么网上很多人说,如果不存在递归调用computeIfAbsent()是不会发生的。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM