什么是快速失败(fail-fast)和安全失败(fail-safe)?

什么是快速失败(fail---fast)和安全失败(fail-safe)

一、快速失败(fail---fast)

  • 现象:在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception(并发修改异常)。

  • 原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。(源码很清晰)

  • 场景:java.util 包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

  • 普通集合容器源码中的Itr在集合采取该方法进行遍历时,
    cursor :调用next方法时,返回的元素的索引值
    lastRet :最近一次调用nextprevious的返回值,如果该元素已经被调用remove方法删除,则重置为-1
    modCount :迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。 集合在被遍历期间如果内容发生变化,就会改变 modCount 的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测 modCount 变量是否为 expectedmodCount 值,是的话就返回遍历;否则抛出异常,终止遍历。

java 复制代码
    private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;
 
        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;
 
        /**
         * The modCount value that the iterator believes that the backing
         * List should have.  If this expectation is violated, the iterator
         * has detected concurrent modification.
         */
        int expectedModCount = modCount;
 
        public boolean hasNext() {
            return cursor != size();
        }
 
        public E next() {
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }
 
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
 
            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }
 
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

二、安全失败(fail-safe)

现象:在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则不会抛出 Concurrent Modification Exception。

原理:采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

三、测试代码:

java 复制代码
public class ListDemo01 {
 
    public static void main(String[] args) {
 
        // 创建集合对象
        List<String> list = new ArrayList<>() ;
        list.add("张三") ;
        list.add("李四") ;
        list.add("王五") ;
 
        // 遍历
        Iterator<String> it = list.iterator();
        while(it.hasNext()) {
            String e = it.next();
            if(e.equals("王五")){
                list.remove(e) ;
            }
        }
 
        // 输出
        System.out.println(list);
    }
 
}
 
public class CopyOnWriteArrayListDemo01 {
 
    public static void main(String[] args) {
 
        // 创建集合对象
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>() ;
        list.add("张三") ;
        list.add("李四") ;
        list.add("王五") ;
 
        // 遍历
        Iterator<String> it = list.iterator();
        while(it.hasNext()) {
            String e = it.next();
            if(e.equals("王五")){
                list.remove(e) ;
            }
        }
 
        // 输出
        System.out.println(list);
 
    }
 
}

安全失败机制的集合容器中的Itr源码

在集合采取该方法进行遍历时,

java 复制代码
        snapshot: 数组的快照

        cursor: 调用next方法时,返回的元素的索引值
       

因为遍历时是通过快照数组进行的遍历,不会影响原有的集合的任何操作

java 复制代码
    static final class COWIterator<E> implements ListIterator<E> {
        /** Snapshot of the array */
        private final Object[] snapshot;
        /** Index of element to be returned by subsequent call to next.  */
        private int cursor;
 
        private COWIterator(Object[] elements, int initialCursor) {
            cursor = initialCursor;
            snapshot = elements;
        }
 
        public boolean hasNext() {
            return cursor < snapshot.length;
        }
 
        public boolean hasPrevious() {
            return cursor > 0;
        }
 
        @SuppressWarnings("unchecked")
        public E next() {
            if (! hasNext())
                throw new NoSuchElementException();
            return (E) snapshot[cursor++];
        }
 
        @SuppressWarnings("unchecked")
        public E previous() {
            if (! hasPrevious())
                throw new NoSuchElementException();
            return (E) snapshot[--cursor];
        }
 
        public int nextIndex() {
            return cursor;
        }
 
        public int previousIndex() {
            return cursor-1;
        }
 
        /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; {@code remove}
         *         is not supported by this iterator.
         */
        public void remove() {
            throw new UnsupportedOperationException();
        }
 
        /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; {@code set}
         *         is not supported by this iterator.
         */
        public void set(E e) {
            throw new UnsupportedOperationException();
        }
 
        /**
         * Not supported. Always throws UnsupportedOperationException.
         * @throws UnsupportedOperationException always; {@code add}
         *         is not supported by this iterator.
         */
        public void add(E e) {
            throw new UnsupportedOperationException();
        }
 
        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            Object[] elements = snapshot;
            final int size = elements.length;
            for (int i = cursor; i < size; i++) {
                @SuppressWarnings("unchecked") E e = (E) elements[i];
                action.accept(e);
            }
            cursor = size;
        }
    }
相关推荐
心之语歌1 小时前
基于注解+拦截器的API动态路由实现方案
java·后端
华仔啊2 小时前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
ray_liang2 小时前
用六边形架构与整洁架构对比是伪命题?
java·架构
Ray Liang3 小时前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
Java水解4 小时前
Java 中间件:Dubbo 服务降级(Mock 机制)
java·后端
阿白的白日梦7 小时前
winget基础管理---更新/修改源为国内源
windows
SimonKing8 小时前
OpenCode AI辅助编程,不一样的编程思路,不写一行代码
java·后端·程序员
FastBean8 小时前
Jackson View Extension Spring Boot Starter
java·后端
Seven979 小时前
剑指offer-79、最⻓不含重复字符的⼦字符串
java
皮皮林55118 小时前
Java性能调优黑科技!1行代码实现毫秒级耗时追踪,效率飙升300%!
java