一提到fail-fast几乎都是java集合里面的Iterator,确实java集合里面的迭代器用到了fail-fast机制,但是fail-fast机制不局限于此 看看维基百科的解释
In systems design, a fail-fast system is one which immediately reports at its interface any condition that is likely to indicate a failure. Fail-fast systems are usually designed to stop normal operation rather than attempt to continue a possibly flawed process. Such designs often check the system's state at several points in an operation, so any failures can be detected early. The responsibility of a fail-fast module is detecting errors, then letting the next-highest level of the system handle them. 在系统设计中,快速失效系统一种可以立即报告任何可能表明故障的情况的系统。快速失效系统通常设计用于停止正常操作, 而不是试图继续可能存在缺陷的过程。这种设计通常会在操作中的多个点检查系统的状态,因此可以及早检测到任何故障。 快速失败模块的职责是检测错误,然后让系统的下一个最高级别处理错误。 复制代码
说白了就是优先考虑异常,发现异常直接停止上报。
BigDecimal类下这个divide方法,在最开始就预判了除数为0的情况,并且直接抛出异常,这就是最好的fail-fast机制的应用。
public BigDecimal divide(BigDecimal divisor) {
/*
* Handle zero cases first.
*/
if (divisor.signum() == 0) { // x/0
if (this.signum() == 0) // 0/0
throw new ArithmeticException("Division undefined"); // NaN
throw new ArithmeticException("Division by zero");
}
//省略其他逻辑... ...
}
复制代码
提到java集合类中fail-fast机制,大概就是抛出ConcurrentModificationException这个异常了。 这里拿ArrayList的iterator()的源码说明一下
1、迭代器iterator()方法执行的时候int expectedModCount = modCount, 2、modCount是AbstractList定义的 protected transient int modCount = 0; 3、迭代器在调用 next()、remove() 方法时都是调用 checkForComodification() 方法, 该方法主要就是检测 modCount == expectedModCount ? 4、ArrayList 中无论 add、remove、clear 方法只要是涉及了改变 ArrayList 元素的个数的方法都会导致 modCount 的改变 复制代码
部分源码
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;//调用iterator()的时候赋值
Itr() {}
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)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} 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();
}
}
复制代码
java.util.concurrent包下的容器都是fail-safe 对应ArrayList的类CopyOnWriteArrayList CopyOnWriteArrayList这个类设计思想是读写分离,写(add/remove)操作是copy一份副本,在这个副本里操作完成之后再用这个副本替换原件。 当然为了避免出现多个副本,写操作是需要加锁的。这个类适用于读多写特别少的情况,否则很容易OOM。
好多文章都说集合的fail-fast是在并发线程中发生的,其实并不一定是多线程,只是多线程更容易出现。 比如下面这段代码就会抛出ConcurrentModificationException foreach循环使用了iterator
private static void test2() {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
for (String str : list) {
list.add("c");
}
for (String str : list) {
System.out.println(str);
}
}
复制代码