【问题标题】:Java forEach lambda throws concurrentModificationExceptionJava forEach lambda 抛出 concurrentModificationException
【发布时间】:2018-05-18 08:37:13
【问题描述】:

下面的方法抛出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;
    }

我觉得它与静态方法及其由多个线程访问的局部变量有关,但根据理论,这将是线程局部变量并且不共享。给我一些时间来写一些简单的例子。

【问题讨论】:

  • 堆栈跟踪说明了什么。
  • 什么是legsallocs等等?
  • @Ben...让我用简单的例子来更新一下。
  • 如何创建Execution 对象的副本?
  • 请发帖minimal reproducible example。是否有可能其中一种方法修改了所涉及的集合之一(例如,executionAlloc.account() 修改了集合?)

标签: java foreach java-8


【解决方案1】:

你的逻辑可以是这样的:

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

【讨论】:

  • 是的,这个代码比 OP 的代码更干净,但是如何进行这个更改来解决 ConcurrentModificationException 问题?
  • @Eran 老实说,我不知道 OP 在哪里可以得到ConcurrentModificationException
  • 由于leg.allocs()的结果有forEach方法,它不是一个数组而是一个集合,所以它应该是leg.allocs().stream()而不是Stream.of(leg.allocs())。此外,如果您在filter 之前执行.map(executionAlloc -&gt; executionAlloc.account()),您将获得很多好处,那么谓词就变成了一个简单的null-check (filter(Objects::nonNull)),而无需重复account() 调用。跨度>
  • 这是正确的@Holger 谢谢你的详细信息,我编辑我的答案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-12
  • 2013-08-09
  • 1970-01-01
  • 1970-01-01
  • 2012-07-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多