Java forEach lambda抛出concurrentModificationException

时间:2018-05-18 08:37:13

标签: java foreach java-8

以下方法抛出ConcurrentModificationException的原因是什么?

static Set<String> setOfAllocAccountsIn(final @NotNull Execution execution) {
        final Set<String> allocAccounts = new HashSet<>();
        execution.legs().forEach(leg -> leg.allocs().forEach(alloc -> {
            if (alloc.account() != null) {
                allocAccounts.add(alloc.account());
            }
        }));
        return allocAccounts;
    }

堆栈追踪:

"java.util.ConcurrentModificationException:
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1382)
    at com.client.stp.util.DealsBuilderUtil.setOfAllocAccountsIn(DealsBuilderUtil.java:146)
    at com.client.stp.builder.GridDealsObjectBuilder.build(GridDealsObjectBuilder.java:47)
    at com.client.stp.processor.DealToPDXMLTransformer.transform(DealToPDXMLTransformer.java:29)
    at com.client.stp.processor.WorkPartitionerEngine.lambda$onEventSubmit$0(WorkPartitionerEngine.java:40)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:514)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.base/java.lang.Thread.run(Thread.java:844)

使用简单for-loop它可以正常工作。该方法由具有自己的执行对象副本的多个线程调用。

解决方案适用于简单的for循环:

 static Set<String> setOfAllocAccountsIn(final @NotNull Execution execution) {
        final Set<String> allocAccounts = new HashSet<>();
        for (final ExecutionLeg executionLeg : execution.legs()) {
            for (final ExecutionAlloc executionAlloc : executionLeg.allocs()) {
                if (executionAlloc.account() != null) {
                    allocAccounts.add(executionAlloc.account());
                }
            }
        }
        return allocAccounts;
    }

我觉得它与静态方法及其多个线程访问的局部变量有关,但理论上它们是线程局部变量而不是共享。给我一些时间来举一些简单的例子。

1 个答案:

答案 0 :(得分:2)

你的逻辑可以是这样的:

return execution.legs().stream()
            .flatMap(leg -> leg.allocs().stream())
            .map(executionAlloc -> executionAlloc.account())
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());
  • 您在forEach内使用forEach(此处我将其替换为flatMap
  • 然后,如果alloc.account()为空,则检查每个元素(我将其替换为filter
  • 然后如果条件正确,则将其添加到Set(我将其替换为collect