什么是快速失败(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;
        }
    }
相关推荐
考虑考虑2 小时前
Jpa使用union all
java·spring boot·后端
用户3721574261352 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊3 小时前
Java学习第22天 - 云原生与容器化
java
渣哥5 小时前
原来 Java 里线程安全集合有这么多种
java
间彧5 小时前
Spring Boot集成Spring Security完整指南
java
间彧5 小时前
Spring Secutiy基本原理及工作流程
java
Java水解6 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆8 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学9 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole9 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端