代码之家  ›  专栏  ›  技术社区  ›  Antoine Claval

番石榴多图和并发修改除外

  •  9
  • Antoine Claval  · 技术社区  · 15 年前

    我不明白当我重复这个时为什么会得到一个ConcurrentModificationException multimap . 我读了以下内容 entry 但是我不确定我是否理解整个事情。 我试图添加一个同步块。但我的疑问是什么时候和什么时候同步。

    这个 多重映射 是一个字段,创建方式如下:

    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++;
        }
    }
    
    5 回复  |  直到 8 年前
        1
  •  2
  •   Evgeny Kiselev    8 年前

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

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

        2
  •  11
  •   Vladimir Matveev    12 年前

    在迭代集合时对其调用Remove将导致每次都发生ConcurrentModificationException,即使所有操作都在同一线程中完成-正确的做法是获取显式迭代器并对此调用.Remove()。

    编辑:修改示例:

    Iterator<Map.Entry<GenericEvent, Command>> i = eventMultiMap.entries().iterator();
    while (i.hasNext()) {
        if (i.next().getValue().equals(command)) {
            i.remove();
            nbRemoved++;
        }
    }
    
        3
  •  4
  •   Jared Levy    15 年前

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

    synchronized (eventMultimap) {
      Iterator<Entry<GenericEvent, Command>> 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块,则多映射可能在removeall()调用和其中一个size()调用之间发生变化,从而导致nbremoved的值不正确。

    现在,如果您的代码是单线程的,并且您只想避免一个ConcurrentModificationException调用,那么您可以省去multimaps.synchronizedMultimap和synchronized(eventmulimap)逻辑。

        4
  •  4
  •   Dimitris Andreou    15 年前

    你可能想看看 this blogpost 因为另一个陷阱 ConcurrentModificationException 当遍历多映射时,没有其他线程干扰。简而言之,如果您遍历multimap的键,访问与每个键关联的值的各自集合,并从这样的集合中删除一些元素, 如果该元素恰好是集合的最后一个元素 你会有 当前修改例外 当您尝试访问下一个键时-因为清空集合会触发删除键,从而在结构上修改多映射的键集。

        5
  •  1
  •   Mr. Polywhirl    11 年前

    我更喜欢 Multimap.values().iterator() 如果你不在乎钥匙。您还应该尽量避免使用同步块,因为您不能有效地区分读/写的优先级。

    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();
      }
    }