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

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