0

I get a ConcurrentModificationException although the piece of code that is listed in the stacktrace doesn't do any modification:

Map<String, ProblemItem> problemItemsMap = getProblemItemsMap();
Optional<ProblemItem> findAny =
    problemItemsMap.values().stream()
    .filter(pi -> pi.getId().equals(id))
    .findAny();

I understand from this answer that it has to do with the HashMap. But as you can see, there is no add or remove or any other modification on the map at this location. I assume the problem occurs because of a modification somewhere else that just happen to be at the same time as the iteration.

The stacktrace looks like this:

 Uncaught exception in thread 'pool-10-thread-1'.java.util.ConcurrentModificationException: null
    at java.base/java.util.HashMap$ValueSpliterator.tryAdvance(HashMap.java:1698)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findAny(ReferencePipeline.java:548)

So I have no clue where the modification takes place and it confuses me that the stacktrace shows the iteration and not the modification.

It's worth mentioning that this exception only occurs once in a while in my multi thread application.

7
  • 5
    "this exception only occurs once in a while in my multi thread application" Then I expect there's another thread modifying your HashMap. Commented Jun 26, 2023 at 13:18
  • 2
    BTW findAny is just the method starting the whole stream evaluation || "as you can see"?!! not really - that for sure is not the whole code! Better post a minimal reproducible example. || documentation: "thrown by methods that have detected concurrent modification of an object when such modification is not permissible . . . it is not generally permissible for one thread to modify a Collection while another thread is iterating over it" Commented Jun 26, 2023 at 13:35
  • 4
    This code is looping over the elements of the map. At each step of the loop, it checks if the map has been modified. If the map has been modified, then you get a CME. You're not getting the exception because this loop modified it, something else modified it while this loop was running. Commented Jun 26, 2023 at 13:35
  • 1
    I am unsure what the comments are suggesting. To lock the map when modifying it or when iterating over it or both or maybe another solution? Commented Jun 26, 2023 at 14:43
  • 2
    I would have gone for the "ConcurrentHashMap " instead of the "Hashmap" in your case. Commented Jun 26, 2023 at 15:16

1 Answer 1

1

EDIT: I suspect that what we are solving is a typical instance of the X-Y problem. Probably the whole code needs refactoring, as it probably does not make much sense working with a map which is constantly changing in the backrgound. What is the expected overall behaviour?


It seems like there are bad guys around conspiring against your thread, modifying the map in the background :)

  1. The best solution, if you can change the code of getProblemItemsMap() or implement your own, would be to reimplement it completely so no one changes it while you use it.

Anyway, from the business logic point of view, how is the behaviour of your code defined if the underlying map is constantly changing?

  1. Copy the map before use:
problemItemsMap = Map.copyOf(getProblemItemsMap());

The documentation says:

Returns an unmodifiable Map containing the entries of the given Map. The given Map must not be null, and it must not contain any null keys or values. If the given Map is subsequently modified, the returned Map will not reflect such modifications.

I am not sure how copyOf is implemented, maybe it will be able to catch a snapshot of the underlying map. I don't know how tolerant it is.

Sign up to request clarification or add additional context in comments.

5 Comments

... and how do you ensure the copying doesn't fail with a ConcurrentModificationException? After all, copying its contents uses the Map, too ...
@meriton haha, that's a valid point :) Actually, I am not sure how copyOf is implemented, maybe it's more tolerant than the stream processing.
In addition, Copying the map is likely not good for performance. Best suggestion so far was to use ConcurrentHashMap.
@NathanHughes I somehow expected that the OP does not have control over getProblemItemsMap(). Of course the re-implementation of this method is the best solution.
Seems likely to me too.

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.