0

What could be the reason that below method throws 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;
    }

Stack Trace:

"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)

With simple for-loop it works fine. The method is called by multiple threads with its own copy of execution object.

Solution which worked with simple for loop:

 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;
    }

I feel that it has something to do with static method and its local variable accessed by multiple threads but per theory that will be thread local variable and are not shared. Give me some time to put down some simple example.

9
  • 2
    What does the stacktrace say. Commented May 18, 2018 at 8:38
  • What is legs, allocs and so on? Commented May 18, 2018 at 8:38
  • @Ben...let me update it with simple example. Commented May 18, 2018 at 8:41
  • how do you create a copy of Execution object? Commented May 18, 2018 at 8:51
  • 2
    Please post a minimal reproducible example. Is it possible that one of the methods modifies one of the collections involved (eg maybe executionAlloc.account() modifies on of the collections?) Commented May 18, 2018 at 9:33

1 Answer 1

2

Your logic can be like this :

return execution.legs().stream()
            .flatMap(leg -> leg.allocs().stream())
            .map(executionAlloc -> executionAlloc.account())
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());
  • You are using forEach inside forEach (Here I replace it with flatMap)
  • then you check for each element if alloc.account() is null or not (I replace it with filter)
  • then if the condition is correct you add it to the Set (I replace it with a collect)
Sign up to request clarification or add additional context in comments.

4 Comments

Yes, this code is cleaner than the OP's code, but how would making this change solve the ConcurrentModificationException?
@Eran honestly I don't see where the OP can get ConcurrentModificationException
Since the result of leg.allocs() has a forEach method, it is not an array but likely a collection, so it should be leg.allocs().stream() instead of Stream.of(leg.allocs()). Further, you gain a lot, if you do .map(executionAlloc -> executionAlloc.account()) before the filter, as then, the predicate becomes a simple null-check (filter(Objects::nonNull)) instead of needing to repeat the account() call.
this is correct @Holger thank you for the details, I edit my answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.