Guava MultiMap和ConcurrentModificationException

我不明白为什么在迭代这个multimap时我得到一个ConcurrentModificationException。 我读了以下条目 ,但我不确定我是否理解了整个事情。 我试图添加一个synchronized块。 但我怀疑的是与什么同步,何时。

multimap是一个字段,创建如下:

 private Multimap eventMultiMap = Multimaps.synchronizedMultimap(HashMultimap. create()); 

并像这样使用:

 eventMultiMap.put(event, command); 

并且像这样(我试图在地图上同步这部分,但没有成功)

 for (Entry entry : eventMultiMap.entries()) { if (entry.getValue().equals(command)) { eventMultiMap.remove(entry.getKey(), entry.getValue()); nbRemoved++; } } 

在java8中,您还可以使用lambda方法:

eventMultiMap.entries().removeIf(genericEventCommandEntry -> genericEventCommandEntry.getValue().equals(command));

在迭代它时对集合调用remove将导致每次都出现ConcurrentModificationException,即使它们都在同一个线程中完成 – 要做的正确事情是得到一个显式迭代器并调用.remove()。

编辑:修改您的示例:

 Iterator> i = eventMultiMap.entries().iterator(); while (i.hasNext()) { if (i.next().getValue().equals(command)) { i.remove(); nbRemoved++; } } 

如果另一个线程在此逻辑运行时可以修改多重映射,则需要向MHarris的代码添加一个synchronized块:

 synchronized (eventMultimap) { Iterator> i = eventMultiMap.entries.iterator(); while (i.hasNext()) { if (i.next().getValue().equals(command)) { i.remove(); nbRemoved++; } } } 

或者,您可以省略迭代器,如下所示,

 synchronized (eventMultimap) { int oldSize = eventMultimap.size(); eventMultimap.values().removeAll(Collections.singleton(command)); nbRemoved = oldSize - eventMultimap.size(); } 

removeAll()调用不需要同步。 但是,如果省略synchronized块,则multimap可能会在removeAll()调用和其中一个size()调用之间发生变化,从而导致nbRemoved值不正确。

现在,如果您的代码是单线程的,并且您只想避免ConcurrentModificationException调用,则可以省略Multimaps.synchronizedMultimap和synchronized(eventMultimap)逻辑。

您可能希望看到此博客文章中的另一个陷阱,在遍历多图时会产生ConcurrentModificationException ,而没有其他线程干扰。 简而言之,如果遍历multimap的键,访问与每个键关联的相应值集合并从这样的集合中删除一些元素, 如果该元素碰巧是集合的最后一个,那么当您尝试访问时,将会出现ConcurrentModificationException下一个键 – 因为清空一个集合会触发删除键,从而在结构上修改多图的键集。

如果你不关心密钥,我更喜欢Multimap.values().iterator() 。 您还应该尝试尽可能远离使用synchronized块,因为您无法有效地优先读取/写入。

 ReadWriteLock lock = new ReentrantReadWriteLock(); Lock writeLock = lock.writeLock(); public void removeCommands(Command value) { try { writeLock.lock(); for (Iterator it = multiMap.values().iterator(); it.hasNext();) { if (it.next() == value) { it.remove(); } } } finally { writeLock.unlock(); } }