hen*_*xin 6 java concurrency multithreading hashmap concurrentmodification
码:
我有一个HashMap
private Map<K, V> map = new HashMap<>();
一种方法是通过调用将KV对放入其中put(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()在第二种方法中使用也行.
您需要提供某种程度的同步,以便在执行呼叫时put阻止对的toArray调用,反之亦然。有三种简单的方法:
put,并toArray以synchronized块,同样的锁定对象(这可能是地图本身或者其它对象)上同步。使用以下工具将地图转换为同步地图 Collections.synchronizedMap()
private Map<K, V> map = Collections.synchronizedMap(new HashMap<>());
使用ConcurrentHashMap而不是HashMap。
编辑:使用的问题Collections.synchronizedMap是一旦调用values()返回,并发保护将消失。此时,对put()和的调用toArray()可能会同时执行。A ConcurrentHashMap也有类似的问题,但仍然可以使用。从文档中获取ConcurrentHashMap.values():
视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出
ConcurrentModificationException,并保证遍历迭代器构造时存在的元素,并且可以(但不保证)反映构造之后的任何修改。