Java 1.8 hashMap 併發put 的問題 java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode

報錯
Caused by: java.lang.ClassCastException: java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode
        at java.util.HashMap$TreeNode.moveRootToFront(HashMap.java:1835)
        at java.util.HashMap$TreeNode.treeify(HashMap.java:1951)
        at java.util.HashMap.treeifyBin(HashMap.java:772)
        at java.util.HashMap.putVal(HashMap.java:644)
        at java.util.HashMap.put(HashMap.java:612)
        at com.core.manager.DetailManager.lambda$execute$0(DetailManager.java:81)
        at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1382)
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
        at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:290)
        at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
        at java.util.concurrent.ForkJoinPool$WorkQueue.execLocalTasks(ForkJoinPool.java:1040)
        at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1058)
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

這個錯誤其實很難復現,除非併發put的量大於20W以上。

原因

我這裏出現這個的問題是使用了Stream.parallel()來進行並行put map的,因爲hashMap不是線程安全的。
造成這個的原因,是此時有兩個線程同時修改一個map中的一個實現了Map.Entry的node對象。線程1發現滿足鏈表轉紅黑樹要進行 treeify操作了,而另一個線程2的此時的map快照還未滿足,仍然當做正常Node使用,此時,線程1進行把node節點轉化爲treeNode對象時候,就會出現這個異常。
在這裏插入圖片描述

解決

在數據量大的時候並行確實不錯,但是要考慮使用線程安全的concurrentHashmap

慎用parallelStream,因爲默認底層是公用同一個線程池的,默認情況下比較適用於一些簡單快速的計算任務,像這種IO查詢的場景不適用。如果剛好有其他的業務也使用parallelStream就坑了。

參考:
https://bugs.openjdk.java.net/browse/JDK-8173671

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章