Guava MultiMap and ConcurrentModificationException
我不明白为什么当我迭代这个
1 2 | private Multimap<GenericEvent, Command> eventMultiMap = Multimaps.synchronizedMultimap(HashMultimap.<GenericEvent, Command> create()); |
使用方法如下:
1 | eventMultiMap.put(event, command); |
像这样(我试着在地图上同步这个部分,但没有成功)
1 2 3 4 5 6 | for (Entry<GenericEvent, Command> entry : eventMultiMap.entries()) { if (entry.getValue().equals(command)) { eventMultiMap.remove(entry.getKey(), entry.getValue()); nbRemoved++; } } |
在迭代集合时对其调用Remove将导致每次都发生ConcurrentModificationException,即使所有操作都在同一线程中完成-正确的做法是获取显式迭代器并对此调用.Remove()。
编辑:修改示例:
1 2 3 4 5 6 7 | Iterator<Map.Entry<GenericEvent, Command>> i = eventMultiMap.entries().iterator(); while (i.hasNext()) { if (i.next().getValue().equals(command)) { i.remove(); nbRemoved++; } } |
您可能希望看到这个blogpost来寻找另一个陷阱,在遍历多映射时产生一个
如果另一个线程可以在运行此逻辑时修改您的多映射,则需要向mharris的代码添加一个同步块:
1 2 3 4 5 6 7 8 9 | synchronized (eventMultimap) { Iterator<Entry<GenericEvent, Command>> i = eventMultiMap.entries.iterator(); while (i.hasNext()) { if (i.next().getValue().equals(command)) { i.remove(); nbRemoved++; } } } |
或者,可以省略下面的迭代器,
1 2 3 4 5 | synchronized (eventMultimap) { int oldSize = eventMultimap.size(); eventMultimap.values().removeAll(Collections.singleton(command)); nbRemoved = oldSize - eventMultimap.size(); } |
removeall()调用不需要同步。但是,如果省略synchronized块,则多映射可能在removeall()调用和其中一个size()调用之间发生变化,从而导致nbremoved的值不正确。
现在,如果您的代码是单线程的,并且您只想避免一个ConcurrentModificationException调用,那么您可以省去multimaps.synchronizedMultimap和synchronized(eventmulimap)逻辑。
在Java8中,还可以使用lambda方法:
如果你不在乎钥匙的话,我更喜欢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ReadWriteLock lock = new ReentrantReadWriteLock(); Lock writeLock = lock.writeLock(); public void removeCommands(Command value) { try { writeLock.lock(); for (Iterator<Command> it = multiMap.values().iterator(); it.hasNext();) { if (it.next() == value) { it.remove(); } } } finally { writeLock.unlock(); } } |