如何避免HashMap" ConcurrentModificationException"在并发线程中操作`values()`和`put()`?

时间:2014-10-29 02:35:04

标签: java multithreading concurrency hashmap concurrentmodification

代码:

我有一个HashMap

private Map<K, V> map = new HashMap<>();

一种方法是通过调用put(K,V)将K-V对放入其中。

另一种方法想从其值中提取一组随机元素:

int size = map.size();    // size > 0
V[] value_array = map.values().toArray(new V[size]);
Random rand = new Random();
int start = rand.nextInt(size); int end = rand.nextInt(size);
// return value_array[start .. end - 1]

这两种方法在两个不同的并发线程中调用。


错误:

我收到ConcurrentModificationException错误:

at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$ValueIterator.next(Unknown Source)
at java.util.AbstractCollection.toArray(Unknown Source)

似乎一个线程中的toArray()方法实际上在HashMap上进行迭代,并且在其他线程中发生put()修改。

  

问题:在并发线程中使用HashMap.values()。toArray()和HashMap.put()时如何避免“ConcurrentModificationException”?   直接避免在第二种方法中使用values().toArray()也没关系。

1 个答案:

答案 0 :(得分:5)

您需要提供某种级别的同步,以便在执行put调用时阻止对toArray的调用,反之亦然。有两种简单的方法:

  1. 将您的呼叫包裹在同一锁定对象(可能是地图本身或其他某个对象)同步的put块中的toArraysynchronized
  2. <击>
  3. 使用Collections.synchronizedMap()

    将地图变为同步地图
    private Map<K, V> map = Collections.synchronizedMap(new HashMap<>());
    

    <击>

  4. 使用ConcurrentHashMap代替HashMap

  5. 编辑:使用Collections.synchronizedMap的问题是,一旦对values()的调用返回,并发保护就会消失。此时,对put()toArray()的调用可能会同时执行。 ConcurrentHashMap有一个类似的问题,但它仍然可以使用。来自ConcurrentHashMap.values()的文档:

      

    视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出ConcurrentModificationException,并保证遍历构造迭代器时存在的元素,并且可能(但不保证)反映后续的任何修改施工。