fail-fast与fail-safe简介
如果一个系统,当有异常或者错误发生时就立即中断执行,这种设计称之为fail-fast
。相反如果我们的系统可以在某种异常或者错误发生时继续执行,不会被中断,这种设计称之为fail-safe
。
fail-fast与fail-safe在Java迭代器中的设计
在Java中,最典型的fail-fast与fail-safe就是关于迭代器的设计。通常情况下,那些线程不安全的集合类产生的迭代器都是fail-fast的,而线程安全的集合类产生的迭代器是fail-safe的。fail-fast的迭代器会在迭代过程中,如果你修改了集合类里的内容,则会抛出ConcurrentModificationException
异常。fail-safe的迭代器则可以在迭代过程中任意修改集合类的内容,不会有异常抛出。
Java的fail-fast迭代器
前面说过,线程安全的集合类产生的迭代器是基于fail-fast设计的,例如ArrayList
。这种迭代器迭代过程中是直接访问原数据信息的,所以当原集合内容修改了后,迭代器不能保证正确的迭代过程。代码示例如下:
public static void failFast() {
List<String> list = new ArrayList<>();
list.add("item-1");
list.add("item-2");
list.add("item-3");
list.add("item-4");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
list.add("itme-5"); // 下次迭代时会抛出ConcurrentModificationException异常
}
}
通过分析ArrayList
源码可以看出,当对ArrayList
做添加或者删除元素的操作时,都会修改modCount
这个变量,而ArrayList
的迭代器每次迭代的时候,又都回去检查当前modCount
和迭代器产生时的expectedModCount
变量是否相等,如果不等就会抛出ConcurrentModificationException
异常。
protected transient int modCount = 0;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
// 上面那个方法调用后会修改modCount
....
}
// ArrayList的迭代器
private class Itr implements Iterator<E> {
public E next() {
checkForComodification();
...
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
...
}
Java的fail-safe迭代器
对于那些线程安全的集合类,在调用iterator
方法产生迭代器的时候,会将当前集合的素有元素都做一个快照,即复制一份副本。每次迭代的时候都是访问这个快照内的元素,而不是原集合的元素。代码示例如下:
public static void failSafe() {
List<String> list = new CopyOnWriteArrayList<>();
list.add("item-1");
list.add("item-2");
list.add("item-3");
list.add("item-4");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
list.add("itme-5");
}
System.out.println(list.size()); // 会打印出来8,迭代四次,四个新元素插入到了集合中。
}
这种设计的好处是保证了在多线程操纵同一个集合的时候,不会因为某个线程修改了集合,而影响其他正在迭代访问集合的线程。缺点是,迭代器不能正确及时的反应集合中的内容,而且一定程度上也增加了内存的消耗。
迭代器小提示
如果用Java的for loop
来访问集合,原理上还是用迭代器的方式,所以下面的代码同样会抛出ConcurrentModificationException
异常。
List<String> list = new ArrayList<>();
list.add("item-1");
list.add("item-2");
list.add("item-3");
list.add("item-4");
for (String item : list) {
System.err.println(item);
list.add("itme-5");
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。