背景:
最近有朋友提到了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()是不會發生的。