代码:
我有一个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()
也没关系。
答案 0 :(得分:5)
您需要提供某种级别的同步,以便在执行put
调用时阻止对toArray
的调用,反之亦然。有三两种简单的方法:
put
块中的toArray
和synchronized
。使用Collections.synchronizedMap()
private Map<K, V> map = Collections.synchronizedMap(new HashMap<>());
击> <击> 撞击>
使用ConcurrentHashMap
代替HashMap
。
编辑:使用Collections.synchronizedMap
的问题是,一旦对values()
的调用返回,并发保护就会消失。此时,对put()
和toArray()
的调用可能会同时执行。 ConcurrentHashMap
有一个类似的问题,但它仍然可以使用。来自ConcurrentHashMap.values()
的文档:
视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出
ConcurrentModificationException
,并保证遍历构造迭代器时存在的元素,并且可能(但不保证)反映后续的任何修改施工。