Guava MultiMap 和 ConcurrentModificationException
我不明白为什么当我迭代此 multimap
时会收到 ConcurrentModificationException。 我阅读了以下内容 条目,但我不确定我是否理解了整个事情。 我尝试添加一个同步块。但我的疑问是同步什么以及何时同步。
multimap
是一个字段,创建如下:
private Multimap<GenericEvent, Command> eventMultiMap =
Multimaps.synchronizedMultimap(HashMultimap.<GenericEvent, Command> create());
并使用如下:
eventMultiMap.put(event, command);
并且像这样(我尝试在地图上同步这部分,但没有成功)
for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) {
if (entry.getValue().equals(command)) {
eventMultiMap.remove(entry.getKey(), entry.getValue());
nbRemoved++;
}
}
I don't understand why I get a ConcurrentModificationException when I iterate through this multimap
.
I read the following entry, but I am not sure if I understood the whole thing.
I tried to add a synchronized block. But my doubt is what to synchronize with, and when.
The multimap
is a field and created like this :
private Multimap<GenericEvent, Command> eventMultiMap =
Multimaps.synchronizedMultimap(HashMultimap.<GenericEvent, Command> create());
and used like this :
eventMultiMap.put(event, command);
and like this ( I tried to synchronize this part on the map, but without success )
for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) {
if (entry.getValue().equals(command)) {
eventMultiMap.remove(entry.getKey(), entry.getValue());
nbRemoved++;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在迭代集合时调用remove每次都会导致ConcurrentModificationException,即使这一切都是在同一个线程中完成的 - 正确的做法是获取一个显式迭代器并对其调用.remove()。
编辑:修改您的示例:
Calling remove on a collection while you're iterating through it will cause a ConcurrentModificationException every time, even if it's all done in the same thread - the Right Thing to do is get an explicit iterator and call .remove() on that.
Edit: Modifying your example:
您可能希望查看这篇博文,了解另一个陷阱遍历多重映射时发生 ConcurrentModificationException,且没有其他线程干扰。简而言之,如果您遍历 multimap 的键,访问与每个键关联的相应值集合,并从该集合中删除某个元素,如果该元素恰好是该集合的最后一个,您将当您尝试访问下一个键时,会出现 ConcurrentModificationException ,因为清空集合会触发该键的删除,从而在结构上修改多重映射的键集。
You may wish to see this blogpost for another pitfall yielding a
ConcurrentModificationException
when traversing a multimap, with no other thread interfering. In short, if you traverse multimap's keys, accessing the respective collection of values associated with each key and remove some element from such a collection, if that element happens to be the last of the collection you are going to haveConcurrentModificationException
when you try to access the next key - because emptying a collection triggers the removal of the key, thus structurally modifying the multimap's keyset.如果另一个线程可以在此逻辑运行时修改您的多重映射,则您需要向 MHarris 的代码中添加一个同步块:
或者,您可以省略迭代器,如下所示,
removeAll() 调用不需要同步。但是,如果省略同步块,多重映射可能会在removeAll() 调用和其中一个size() 调用之间发生变化,从而导致nbRemoved 值不正确。
现在,如果您的代码是单线程的,并且您只是想避免 ConcurrentModificationException 调用,则可以省略 Multimaps.synchronizedMultimap 和同步 (eventMultimap) 逻辑。
If another thread could modify your multimap while this logic is running, you'll need to add a synchronized block to MHarris's code:
Or, you could omit the iterator as follows,
The removeAll() call doesn't require synchronization. However, if you omit the synchronized block, the multimap could mutate between the removeAll() call and one of the size() calls, leading to an incorrect value of nbRemoved.
Now, if your code is single-threaded, and you just want to avoid a ConcurrentModificationException call, you can leave out the Multimaps.synchronizedMultimap and synchronized (eventMultimap) logic.
在java8中,您还可以使用lambda方法:
eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));
In java8 you can also use a lambda approach:
eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));
如果您不关心密钥,我更喜欢
Multimap.values().iterator()
。您还应该尽量避免使用同步块,因为您无法有效地优先考虑读/写。I prefer
Multimap.values().iterator()
if you don't care about the key. You should also try to stay away from using synchronized blocks as much as possible, because you cannot prioritize reads/writes effectively.