代码之家  ›  专栏  ›  技术社区  ›  user149408

同步映射密钥集上的线程安全迭代

  •  0
  • user149408  · 技术社区  · 7 年前

    在我的多线程代码中的某个地方,我有一个声明如下的映射(可由多个线程访问):

    private Map<Foo, Integer> fooMap =
        Collections.synchronizedMap(new HashMap<Foo, Integer>());
    

    同一个类公开一个公共方法

    public Collection<Foo> getFooList() {
        return fooMap.keySet();
    }
    

    在我的代码中的其他地方,我对 getFooList() . 我知道,同步映射上的大多数操作都是线程安全的,其中一个显著的异常是迭代,必须显式同步迭代。我已经通过以下方式实现了这一点:

    synchronized(bar.getFooList()) {
        for (Foo foo : bar.getFooList()) {
            // do stuff with foo
        }
    }
    

    偶尔我会得到一个 ConcurrentModificationException 对于 for 语句。我想知道我是否正在与错误的类实例同步我是否应该与映射而不是其密钥集同步?再说一次,我真的不想将整个映射公开给其他类(因为某种原因,它是私有的)。

    如何以线程安全的方式迭代密钥集,而不必公开整个映射?

    1 回复  |  直到 7 年前
        1
  •  3
  •   tsolakp    7 年前

    Javadoc :

    用户必须手动同步返回的 在其任何集合视图上迭代时映射:

      Map m = Collections.synchronizedMap(new HashMap());
          ...
      Set s = m.keySet();  // Needn't be in synchronized block
          ...
      synchronized (m) {  // Synchronizing on m, not s!
          Iterator i = s.iterator(); // Must be in synchronized block
          while (i.hasNext())
              foo(i.next());
      }
    

    不遵循此建议可能导致不确定性 行为。如果指定的映射 是可序列化的。

    你可以使用 ConcurrentHashMap 它保证了 keySet . 见 here :

    视图的迭代器是一个“弱一致”迭代器,永远不会 抛出ConcurrentModificationException,并保证遍历 在迭代器构造时存在的元素,并且可以 (但不保证)反映 建设。