報錯
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