我有下面的代码但是我得到了ConcurrentModificationException,我应该如何避免这个问题? (出于某种原因,我必须使用WeakHashMap)
WeakHashMap<String, Object> data = new WeakHashMap<String, Object>();
// some initialization code for data
for (String key : data.keySet()) {
if (data.get(key) != null && data.get(key).equals(value)) {
//do something to modify the key
}
}
答案 0 :(得分:0)
当不再实现密钥的普通使用时,会自动删除WeakHashMap的条目,这可能发生在不同的线程中。将keySet()
克隆到不同的Set
并发线程中的同时可以删除条目,在这种情况下,ConcurrentModificationException
将被100%抛出!您必须同步克隆。
示例:
Collections.synchronizedMap(data);
请理解
Collections.synchronizedSet(data.keySet());
可以不,因为data.keySet()
依赖于此处未同步的数据实例!更多细节:synchronize(keySet)阻止在keySet上执行方法,但是从不调用keySet的remove-method,而是调用WeakHashMap的remove-method,因此你必须通过WeakHashMap进行同步!
答案 1 :(得分:0)
Javadoc for WeakHashMap类解释了为什么会发生这种情况:
地图不变式不适用于此类。因为垃圾 收集器可以随时丢弃密钥,因此WeakHashMap的行为可能与 尽管一个未知线程正在悄悄地删除条目
此外,根据该javadoc中引用的说明,由您使用的增强的for循环在幕后生成的迭代器具有快速失败类型。
由集合的迭代器方法返回的迭代器 所有此类的“集合视图方法”返回的是 快速失败:如果地图在发布后的任何时间进行了结构修改 迭代器是通过其他方式创建的,除非通过迭代器自己的 remove方法,迭代器将抛出一个 ConcurrentModificationException。因此,面对并发 修改后,迭代器会快速干净地失败,而不是 在不确定的时间冒任意,不确定的行为的风险 将来。
因此,出于以下原因,您的循环可以引发此异常:
由于您的意图似乎是在处理尚未通过GC进行处理的对象,因此我建议如下使用迭代器:
game_is_going = True
答案 2 :(得分:0)
您可以先克隆密钥集,但请注意,您在此之后持有强引用:
Set<KeyType> keys;
while(true) {
try {
keys = new HashSet<>(weakHashMap.keySet());
break;
} catch (ConcurrentModificationException ignore) {
}
}
for (KeyType key : keys) {
// ...
}
答案 3 :(得分:-1)
可能是因为迭代中的// do something
实际上正在修改底层集合。
来自ConcurrentModificationException:
例如,如果一个线程在使用失败快速迭代器迭代集合时直接修改了一个集合,那么迭代器将抛出此异常。
返回此地图中包含的键的Set视图。该集由地图支持,因此对地图的更改将反映在集中,反之亦然。如果在对集合进行迭代时修改了映射(除非通过迭代器自己的remove操作),迭代的结果是未定义的。