错误堆栈如下
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:922)
at java.util.HashMap$KeyIterator.next(HashMap.java:956)
...
hashMap的相关实现源码如下:
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
显然,工程中使用了迭代器遍历hashMap时并且map的size被修改过了,导致modCount != expectedModCount,原因是hash迭代器在将modCount赋值给expectedModCount后,modCount的值被其它线程修改掉了。
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
if (size > 0) { // advance to first entry
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
解决方法:使用ImmutableMap进行线程安全的复制。
hashMap = ...
copyUserMap = ImmutableMap.copyOf(hashMap);
Iterator<String> keys = copyUserMap.keySet().iterator();
while (keys.hasNext()) {
...
}
由于HashMap是线程不安全的,这样在遍历的时候不会因为多线程的修改map而导致并发异常。除了该方法,还可以使用concurrentHashMap,获取线程锁解决。