英文:
ConcurrentModificationException when using java streams without modification
问题
我遇到ConcurrentModificationException异常,尽管堆栈跟踪中列出的代码部分没有进行任何修改:
Map<String, ProblemItem> problemItemsMap = getProblemItemsMap();
Optional<ProblemItem> findAny =
problemItemsMap.values().stream()
.filter(pi -> pi.getId().equals(id))
.findAny();
我从这个答案了解到这与HashMap有关。但正如您所见,此位置没有对地图进行添加、删除或任何其他修改。我认为问题发生是因为在迭代同时发生了其他地方的修改。
堆栈跟踪如下所示:
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)
所以我不知道修改发生在哪里,而且让我困惑的是堆栈跟踪显示了迭代而不是修改。
值得一提的是,这个异常只在我的多线程应用程序中偶尔发生。
英文:
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.
答案1
得分: 1
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
- 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?
- 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.
英文:
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
- 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?
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论