1.1 HashMap的fail-fast机制
代码展示
/**
* HashMap的一个内部临时计数变量,用于HashIterator中快速失败
*/
transient int modCount;
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {}
final Node<K,V> nextNode() {}
public final void remove() {}
}
我们知道 java.util.HashMap 不是线程安全的,因此如果在使用迭代器的过程中有其他线程对Map做了修改,那
将抛出 ConcurrentModificationException,这就是所谓 fail-fast 策略。
fail-fast 机制是 java 集合(Collection)中的一种错误机制。 当多个线程对同一个集合的内容进行操作时,就可能
会产生 fail-fast 事件。
例如:当某一个线程 A 通过 iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程 A
访问集合时,就会抛出 ConcurrentModificationException 异常,产生 fail-fast 事件。
这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对 HashMap 内容(其他集合也会有,例如 ArrayList)的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。
在迭代过程中,迭代器首先会获得几个修改个数,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了Map ,就会抛出ConcurrentModificationException异常。如果必须在迭代过程中修改元素,那么可以使用HashIterator的remove方法,对元素进行操作。那么问题来了,迭代器的remove方法会不会对原集合的元素个数做出修改呢?我们一起看看源码实现。
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);// 调用原集合的removeNode方法,即原集合也会修改
expectedModCount = modCount;
}