1、什么是Fail-Fast(快速失败机制)
快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
fail-fast产生的原因就在于程序在对 collection 进行迭代时,某个线程对该 collection 在结构上对其做了修改,这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。
2、源码阅读
//ArrayList()迭代机制
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
protected int limit = ArrayList.this.size;
int cursor; // next 元素角标
int lastRet = -1; // last 元素角标
int expectedModCount = modCount; //重点:将modCount的值赋值给expectedModCount(期待修改数量)
// 判断是否有下一个元素
public boolean hasNext() {
return cursor < limit;
}
@SuppressWarnings("unchecked")
public E next() { // 获取到next元素
// 1. 当modCount != expectedModCount时,会抛出ConcurrentModificationException异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
int i = cursor;
// 2.角标越界检测
if (i >= limit)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
// 3.cursor的值自增1
cursor = i + 1;
// 4.对lastRet进行赋值并将当前角标对应的数据元素return掉
return (E) elementData[lastRet = i];
}
public void remove() {
// 1.角标越界检测
if (lastRet < 0)
throw new IllegalStateException();
// 2. 当modCount != expectedModCount时,会抛出ConcurrentModificationException异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
try {
// 3.调用arraylist的remove方法,进行remove操作
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
// 4.重点:在3 处调用arraylist的remove方法,进行remove操作时,会将modCount的值自增1,在这里重新对expectedModCount进行赋值操作,否则会导致modCount != expectedModCount,抛出ConcurrentModificationException异常。
expectedModCount = modCount;
limit--;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}