对同步映射的键集进行线程安全迭代

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


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。我想知道我是否正在与错误的类实例同步——我应该与地图而不是它的键集同步吗?再说一次,我真的不想将整个地图公开给其他类(它是私有的,这是有原因的)。


如何以线程安全的方式迭代键集,而不必暴露整个映射?


天涯尽头无女友
浏览 70回答 1
1回答

慕桂英3389331

来自Javadoc:当迭代其任何集合视图时,用户必须在返回的地图上手动同步:&nbsp; Map m = Collections.synchronizedMap(new HashMap());&nbsp; &nbsp; &nbsp; ...&nbsp; Set s = m.keySet();&nbsp; // Needn't be in synchronized block&nbsp; &nbsp; &nbsp; ...&nbsp; synchronized (m) {&nbsp; // Synchronizing on m, not s!&nbsp; &nbsp; &nbsp; Iterator i = s.iterator(); // Must be in synchronized block&nbsp; &nbsp; &nbsp; while (i.hasNext())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; foo(i.next());&nbsp; }不遵循此建议可能会导致不确定的行为。如果指定的映射是可序列化的,则返回的映射将是可序列化的。您可以使用ConcurrentHashMapwhich 保证并发keySet。见这里:视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出 ConcurrentModificationException,并保证在构造迭代器时遍历元素,并且可以(但不保证)反映构造后的任何修改。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java