1

我有下面的代码,但我得到 ConcurrentModificationException,我应该如何避免这个问题?(出于某种原因,我必须使用 Wea​​kHashMap)

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
        } 
    }
4

4 回答 4

1

WeakHashMap类的Javadoc解释了为什么会发生这种情况:

映射不变量不适用于此类。因为垃圾收集器可能随时丢弃键,所以 WeakHashMap 可能表现得好像一个未知线程正在默默地删除条目

此外,根据该 javadoc 中引用的解释,您正在使用的增强型 for 循环在后台生成的迭代器属于快速失败类型。

由此类的所有“集合视图方法”返回的集合的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对映射进行结构修改,除了通过迭代器自己的删除之外的任何方式方法,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

因此,由于以下原因,您的循环可能会引发此异常:

  1. 垃圾收集器已删除键集中的对象。
  2. 代码之外的东西向该地图添加了一个对象。
  3. 循环内部发生了修改。

由于您的意图似乎是处理尚未 GC 的对象,我建议使用如下迭代器:

Iterator<String> it = data.keySet().iterator();
int count = 0;
int maxTries = 3;
while(true) {
    try {
        while (it.hasNext()) {
            String str = it.next();
            // do something
        }
        break;
    } catch (ConcurrentModificationException e) {
        it = data.keySet().iterator(); // get a new iterator
        if (++count == maxTries) throw e;
    }
}
于 2020-01-29T05:22:40.230 回答
0

您可以先克隆密钥集,但请注意,在那之后您持有强引用:

Set<KeyType> keys;
while(true) {
   try {
       keys = new HashSet<>(weakHashMap.keySet());
       break;
   } catch (ConcurrentModificationException ignore) {
   }
}

for (KeyType key : keys) {
    // ...
}
于 2021-02-11T01:51:15.273 回答
-1

WeakHashMap 的条目在不再实现密钥的普通使用时会自动删除,这可能发生在不同的线程中。虽然克隆keySet()到不同Set的并发线程可能会同时删除条目,在这种情况下,ConcurrentModificationException将 100% 被抛出!您必须同步克隆。

例子:

Collections.synchronizedMap(data);

请理解

Collections.synchronizedSet(data.keySet());

不能使用,因为依赖这里没有同步的数据实例data.keySet()更多细节: synchronize(keySet) 阻止在 keySet 上执行方法,但从未调用过 keySet 的 remove-method,但会调用 Wea​​kHashMap 的 remove-method,因此您必须通过 WeakHashMap 进行同步!

于 2017-03-21T01:32:33.680 回答
-2

可能是因为您// do something在迭代中实际上是在修改底层集合。

ConcurrentModificationException

例如,如果线程在使用快速失败迭代器迭代集合时直接修改了集合,则迭代器将抛出此异常。

(弱)HashMap 的 keySet()

返回此映射中包含的键的 Set 视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。如果在对集合进行迭代时修改了映射(通过迭代器自己的删除操作除外),则迭代的结果是不确定的。

于 2013-08-06T02:29:55.193 回答