报错
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