HashMap不是同步的,为什么会出现ConcurrentModificationException异常?

4

HashMap不应该是线程安全的,那么为什么如果有人修改了hashMap,迭代器会抛出concurrentmodificationexception

而ConcurrentHashMap则不会抛出此异常。

迭代器的实现是否对不同的数据结构不同,还是这些数据结构中有某些方法会抛出ConcurrentModificationException

4个回答

3

当在遍历HashMap时修改了其结构(例如添加或删除条目),迭代器可能会出现多种故障。

ConcurrentModificationException异常旨在使任何迭代器由于这些修改而快速失败。

这就是modCount字段的作用:

/**
 * The number of times this HashMap has been structurally modified
 * Structural modifications are those that change the number of mappings in
 * the HashMap or otherwise modify its internal structure (e.g.,
 * rehash).  This field is used to make iterators on Collection-views of
 * the HashMap fail-fast.  (See ConcurrentModificationException).
 */
transient int modCount;

这种行为不仅限于 Map,在迭代过程中修改元素的 Collection 也会抛出此异常。


3

不要被误导以为Concurrent仅与多线程有关。

该异常意味着在使用修改前创建的迭代器时,集合已经进行了结构性修改。这个修改可能是由不同的线程执行的,但也可能是执行迭代的同一个线程。

以下单线程代码会产生CME:

Map<String, String> map = new HashMap<>();
map.put("a", "a");
map.put("b", "b");

for (String k: map.entrySet()) {
  map.clear();
}

或者,更清晰地在循环中显示迭代器(这两个循环是等价的):
Iterator<String> it = map.entrySet().iterator();
while (it.hasNext()) {
  String k = it.next();
  map.clear();
}

在调用map.clear()之后调用it.hasNext()会导致ConcurrentModificationException异常。

0

迭代器在不同的数据结构中实现是否不同,或者这些数据结构中有一种方法会抛出ConcurrentModificationException异常?

是的,Collection类中有不同的Iterator实现。

例如,HashMap类(内部使用HashIterator),ConcurrentHashMap(内部使用KeyIteratorValueIterator等)ArrayList(使用AbstractList中的Iterator)等。

HashMap的迭代器与ConcurrentHashMap的迭代器实现不同。

HashMap的迭代器维护一个版本号(expectedModCount),并验证checkForComodification(),如下所示:

final void checkForComodification() {
   if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

因此,在迭代过程中,如果底层集合的大小被修改(通过添加/删除元素),那么Iterator会像上面所示一样抛出ConcurrentModificationException

ConcurrentHashMapIterator实现不执行上述检查,因此它不会抛出ConcurrentModificationException。您还可以从API ConcurrentHashMap here找到相同的观点。

它们(ConcurrentHashMaps)不会抛出ConcurrentModificationException。但是,迭代器只设计供一个线程使用。


0

由于HashMap是一种快速失败的集合(java.util包下的所有集合都是快速失败的),如果对结构或键进行任何修改,迭代器会抛出异常,主要是在HashMap的情况下。

我们在HashMap中有一个变量来计算Map中的修改次数。使用该迭代器可以跟踪对集合所做的修改次数。

transient volatile int modCount;

请阅读下面的链接:差异已经很好地解释了: Java Modcount (ArrayList) 好的例子: http://www.journaldev.com/122/java-concurrenthashmap-example-iterator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接