fail-fast 机制,即快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出 ConcurrentModificationException异常。fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大努力去抛出,所以这种机制一般仅用于检测bug。
fail-fast出现场景import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class fail_fast {
public static void main(String[] args) {
List list = new ArrayList<>();
for (int i = 0 ; i < 10 ; i++ ) {
list.add(i+"");
}
Iterator iterator = list.iterator();//通过集合得到迭代器对象
while(iterator.hasNext()) {
String i=(String)iterator.next();
if (i.equals("3")) {
list.remove(3);//使用集合对象修改集合元素
}
System.out.println(i);
}
}
}
由上段程序及其结果可知,在迭代器遍历过程中,如果使用集合对象来改变集合里的元素,则会产生ConcurrentModificationException异常。
源码涉及的重要变量:
| 变量名 | 意义 |
|---|---|
| int cursor | 即将遍历的元素的索引 |
| int lastRet | 刚刚遍历过的元素的索引,默认值为-1 |
| int expectedModCount | 预期修改次数,初始值为ArrayList中的modCount |
ArrayList中的modCount用于记录集合操作过程中作的修改次数
Iterator其实只是一个接口,具体的实现还是要看具体的集合类中的内部类去实现Iterator并实现相关方法,这里以ArrayList类为例,查看其源码:
public Iteratoriterator() { return new Itr(); }
Itr类是ArrayList的内部类,实现了Iterator接口,下面是该类的源码:
private class Itr implements Iterator {
int cursor; // index of next element to return 即将遍历的元素的索引
int lastRet = -1; // index of last element returned; -1 if no such 上次遍历的元素的索引
int expectedModCount = modCount;//修改计数器期望值
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor; //此时的游标,指向的是本次要遍历的对象
if (i >= size)//越界了
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)// 如果没有next()操作就直接remove的话,lastRet保持默认值,-1,会抛异常
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;// 为了防止ConcurrentModificationException异常,手动修复了修改计数的期望值
} 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;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
上面程序Itr中hasNext()方法解析:
public boolean hasNext() {
return cursor != size;
}
表示当即将遍历的元素的索引等于集合元素个数,则遍历完成。
上面程序Itr中的next()方法解析:
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
我们可以看到next()方法里,在实际访问元素之前,都会先调用checkForComodification()方法,其源码如下
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
当集合实际修改次数不等于预期修改次数则抛异常,但是expectedModCount初始值默认等于modCount,expectedModCount在整个迭代过程除了一开始赋予初始值modCount外,并没有再发生改变,所以在迭代过程中modCount发生了改变(ArrayList进行add,remove,clear等涉及到修改集合中的元素个数的操作时,modCount就会发生改变(modCount ++))
避免fail-fast在使用迭代器对象的remove()方法删除上次next()方法返回的元素时,并不会发生异常
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class fail_fast {
public static void main(String[] args) {
List list = new ArrayList<>();
for (int i = 0 ; i < 10 ; i++ ) {
list.add(i+"");
}
Iterator iterator = list.iterator();//通过集合得到迭代器对象
while(iterator.hasNext()) {
String i=(String)iterator.next();
if (i.equals("3")) {
// list.remove(3);
iterator.remove();//上次iterator.next()返回的应该是3
System.out.println(list);
}
// System.out.println(i);
}
}
}
ArrayList中迭代器的remove方法的源码:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
为了防止ConcurrentModificationException异常,手动修复了修改计数的期望值,所以无论modCount如何改变都不会产生异常。
产生异常的checkForComodification()方法,其源码如下:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}



