在我的多线程代码的某处,我有一个映射(由多个线程访问)声明如下: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 回答
慕桂英3389331
TA贡献2036条经验 获得超8个赞
来自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());
}
不遵循此建议可能会导致不确定的行为。如果指定的映射是可序列化的,则返回的映射将是可序列化的。
您可以使用ConcurrentHashMapwhich 保证并发keySet。见这里:
视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出 ConcurrentModificationException,并保证在构造迭代器时遍历元素,并且可以(但不保证)反映构造后的任何修改。
添加回答
举报
0/150
提交
取消