fail_fast_fail_safe_iterators

第十章 Fail-Fast迭代器和Fail-Safe迭代器(Iterators)

1 简介

Fail-Fast和Fail-Safe是两种异常处理策略。在一个Fail-Fast系统中,当有异常或者错误发生时,该系统会立即停止运行。Fail-Fast系统的设计理念是系统不应该运行于错误的状态中。因为,如果系统已处于错误状态中,该系统再次发生错误的几率会大大提高,进而可能造成更大的损失。所以,为了避免发生更多的错误,Fail-Fast系统会立即停止并报告发生的错误。而Fail-Safe系统可以容忍和隔离异常或者错误,甚至等待错误恢复。Fail-Fast系统的设计理念是系统应尽可能保持运行状态,提供更多的服务,保持系统更高的系统可靠性(Reliability)和容错性(Fault Tolerance)。

至今为止,Fail-Fast和Fail-Safe策略孰优孰劣仍是一个热门的话题。但总的来说,这两种策略的选择应依应用场景而定。

2 Fail-Fast迭代器和Fail-Safe迭代器(Iterators)

Java语言将Fail-Fast和Fail-Safe的概念引入迭代器的设计之中。迭代器主要用于遍历容器(Collection)中的对象。(例如,常用的容器有链表(List),集合(Set),和Map类)。当在遍历过程中出现异常时,Fail-Fast迭代器会立即终止遍历,并抛出异常;而Fail-Safe迭代器则可以继续运行。

2.1 Fail-Fast迭代器

Fail-Fast迭代器是Java语言中最为常用的迭代器。在遍历的过程中,容器的内容是不允许发生变化的(例如:添加或者删除一个元素)。如果在遍历过程中,容器的内容发生了变化,则会抛出异常。

从实现的角度看,容器对象中会保存一个修改的计数器。当从容器创建迭代器时,迭代器会获得该计数器的一份拷贝。当在遍历过程中调用hasNext()方法时,Java虚拟机会比较容器中的计数器是否与迭代器中的计数器保持一致。如果容器中发生了修改,则会抛出ConcurrentModificationException异常。ConcurrentModificationException异常是一种Unchecked异常(Unchecked Exception)

LinkedList, ArrayList, HashMap创建的迭代器都是Fail-Fast迭代器。例如:

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
 
public class FailFastIteratorExample 
{
    public static void main(String[] args) 
    {
        List<Integer> list = new ArrayList<Integer>();
        list.add(0);    
        list.add(1);
        list.add(2);

        Iterator<Integer> it1 = list.iterator();
        while (it1.hasNext()){
            System.out.println(it1.next());
        }

        Iterator<Integer> it2 = list.iterator();
        while (it2.hasNext()) {  // 抛出ConcurrentModificationException异常
            Integer i = it2.next();
            System.out.println(i);
            list.remove(i);       
        }
    }   
}

2.2 Fail-Safe迭代器

在遍历的过程中,有时需要根据容器当前的内容来修改容器的内容。如果此时使用Fail-Fast迭代器遍历该容器,如果在遍历的过程中容器被修改了,则Java虚拟机会抛出异常。另一种常见的应用场景是多线程并行的情况下,一个线程在遍历容器,而另一个线程可能会修改该容器。为了确保程序正确运行,这两个线程不能同时“访问”该容器。因此,为了方便开发这种应用场景,Java标准库还提供了Fail-Safe迭代器。

当容器发生变化时,Fail-Safe迭代器不会抛出异常。从实现的角度上看,这是因为它们遍历的是容器的拷贝。因此,容器的变化并不会改变容器的拷贝。显然,这种实现的代价是需要额外的保存一份容器的拷贝。而且,在遍历过程中,如果容器插入了新元素,这种迭代器无法遍历这个新元素。

一个著名的Fail-Safe迭代器是CopyOnWriteArrayList构造的迭代器。例如:

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Iterator;
import java.util.List;
 
public class FailSafeIteratorExample 
{
    public static void main(String[] args) 
    {
        List<Integer> list = new CopyOnWriteArrayList<Integer>();
        list.add(0);    
        list.add(1);
        list.add(2);

        Iterator<Integer> it1 = list.iterator();
        while (it1.hasNext()){
            System.out.println(it1.next());
        }

        Iterator<Integer> it2 = list.iterator();
        while (it2.hasNext()) {
            Integer i = it2.next();
            System.out.println(i);
            list.remove(i); 
        }
    }   
}

3 结语

本章介绍了Java语言两种迭代器。在遍历过程中,Fail-Fast迭代器不允许容器有任何修改。这也是最为通用的应用场景。而Fail-Safe迭代器则允许在遍历的过程中容器发生变化。这种实现的代价是需要额外的时间和空间来创建和保存容器的一份拷贝。因此,这种迭代器不宜用于较大的容器。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.