简言之,快速失败。我们在设计一个模块(可以是一个函数,一个类甚至是一个服务)时,如果有某种条件会导致模块无法正常运行下去,就应该让模块立刻终止(可以是return
,也可以抛一个runtimeexception
)。这样做有两种好处:
1)及早终止,防止错误一直被带到下游,导致下游服务崩溃或者产生脏数据;
2)便于排查,fail-fast一般都会有类似断言的机制来判定某种条件,在断言处终止可以很方便的定位问题,而如果错误被带到下游,就很难定位问题的根源了;
与fail-fast相反,如果模块遇到某种错误,不应该让程序失败,而是采取某种降级策略,尽量往下走。这个原则主要适用于主模块内的分支流程。
举个例子,我有一个接口,主要是返回feed流的,但是有一个分支逻辑是调用广告服务插入广告类型的feed的,如果广告服务不可用了,我的整体逻辑是不能挂的,因为不插入广告也没关系,但是正常的feed流还是需要返回。所以对广告服务的调用是要能降级的。简单来说,可以try-catch住,如果调用有异常了就不插入广告。不能因为一个分支逻辑而把主要逻辑整挂了。
这两种原则在设计模块以及编码时需要注意。
fail-fast 机制是java集合(Collection)中的一种错误机制,当单线程或者多个线程在集合遍历过程中对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
单线程例如:当通过iterator
去遍历某集合时,在遍历过程中修改集合中的元素添加、删除就会导致Fail-Fast
代码如:
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
list.remove(iterator.next()); //此步调用会导致Fail-Fast
}
}
多线程例如:当某一个线程A通过iterator
去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException
异常,产生fail-fast事件。
代码如:
public class Main {
private static ArrayList arrayList = new ArrayList();
private static void Print() {
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
arrayList.add(i);
Print();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 5; i < 10; i++) {
arrayList.add(i);
Print();
}
}
});
thread1.start();
thread2.start();
}
}
fail-fast机制,是一种错误检测机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生
想解决Fail-Fast只需要使用java.util.concurrent
下面的包去替换java.util
下面的包