ConcurrentModificationException 在不修改的情况下使用 Java 流时发生。

huangapple go评论72阅读模式
英文:

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&lt;String, ProblemItem&gt; problemItemsMap = getProblemItemsMap();
Optional&lt;ProblemItem&gt; findAny =
    problemItemsMap.values().stream()
    .filter(pi -&gt; 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 &#39;pool-10-thread-1&#39;.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 ConcurrentModificationException 在不修改的情况下使用 Java 流时发生。

  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.

英文:

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 ConcurrentModificationException 在不修改的情况下使用 Java 流时发生。

  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.

huangapple
  • 本文由 发表于 2023年6月26日 21:10:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76557016.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定