转载

fail-fast机制

一提到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");
        }
        //省略其他逻辑... ...
   }
复制代码

再来看普遍存在的集合类中fail-fast

提到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();
        }
    }
复制代码

fail-safe及解决之道

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);
        }
    }
复制代码
原文  https://juejin.im/post/5da92c64518825104d08d1df
正文到此结束
Loading...