fail-fast

fail-fast机制

fail-fast(快速失败)机制在Java集合中都存在,本文就ArrayList分析。

fail-fast是什么?

先从ArrayList的官方解释看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* The iterators returned by this class's {@link #iterator() iterator} and
* {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a>
* if the list is structurally modified at any time after the iterator is
* created, in any way except through the iterator's own
* {@link ListIterator#remove() remove} or
* {@link ListIterator#add(Object) add} methods, the iterator will throw a
* {@link ConcurrentModificationException}. Thus, in the face of
* concurrent modification, the iterator fails quickly and cleanly, rather
* than risking arbitrary, non-deterministic behavior at an undetermined
* time in the future.
*
* <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
* as it is, generally speaking, impossible to make any hard guarantees in the
* presence of unsynchronized concurrent modification. Fail-fast iterators
* throw {@code ConcurrentModificationException} on a best-effort basis.
* Therefore, it would be wrong to write a program that depended on this
* exception for its correctness: <i>the fail-fast behavior of iterators
* should be used only to detect bugs.</i>
*
*/

翻译:
在迭代器创建之后,集合有机构性修改,除非使用迭代器自身的addremove动作,使用ArrayList上的方法对集合中数据进行任何操作 (包括查询和修改), 都会触发快速失败机制,表现为抛出ConcurrentModificationExeception 异常… 迭代器的快速失败并不能保证,即存在有错误发生也没触发的情况,所以,不该依赖这个依赖异常来写程序,仅是用来检查程序是否有bug

通俗的个人理解:

快速失败机制,对于集合上的操作,比如查询,修改,若数据出现不确定的情况,便不再返回一个不确定的结果,而是直接抛出异常,让用户尽快 知道有错误发生了。符合程序的确定性的原则。

例子:

遍历过程中使用ArrayList的方法删除元素,在下一次调用迭代器的next()方法时,抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
//初始一个集合并设置数据
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
list.add(i+"");
}

//形式1:增强型For遍历
for ( String e :list) { //以增强For来遍历集合时,会调用集合内部的迭代器
if (e.equals("1")) { //遍历过程中使用ArrayList的方法删除元素,在下一次调用迭代器的next()方法时,抛出异常
list.remove(e);
}
System.out.println(e);
}

Java中,若是使用增强型For,遍历集合类时,本质使用其内部的迭代器来遍历

1
2
3
4
5
6
7
8
9
//形式2:增强型For的本质形式(去语法糖)
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String e = lit.next();
if (e.equals("1")) {
list.remove(e); //使用ArrayList的remove方法
}
System.out.println(e);
}

正确用法

1
2
3
4
5
6
7
8
9
//形式3,使用自身的迭代器删除,不会抛出异常
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String e = lit.next();
if (e.equals("1")) {
lit.remove(); //调用迭代器自身的remove方法,
}
System.out.println(e);
}

内部实现原理:

List类中有一个变量modCount,记录当前集合结构性修改的次数,(比如增加元素,删除元素都会导致结构性修改),初始为0,在add(), remove()方法中都会调用modCount++。 迭代器Iterator中有一个变量expectedModCount,用于跟踪List中的modCount的值,正常情况下,这两个值是保持相等的,这样可保证 迭代器能够正确无误的遍历所有的元素。若是List自己的删除一个元素,ModCount加1,而迭代器中的ExpectedModCount并没有做修改, 迭代器调用next()方法时,检查到两个值不相同,即抛出异常,表示迭代器已经无法保证正确遍历元素了。 若想要遍历中删除,就要使用该迭代器 自身的remove方法,该方法会修改expectedModCountmodCount相等。