什么是快速失败(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 小时前
Lua 第11部分 小插曲:出现频率最高的单词
开发语言·lua
梦之马1 小时前
spring boot 2升级3 记录
java·spring boot·后端
yuren_xia2 小时前
Spring MVC中自定义日期类型格式转换器
java·spring·mvc
泽02022 小时前
C++入门(缺省参数/函数/引用)
开发语言·c++
GottdesKrieges3 小时前
OceanBase数据库磁盘空间管理
java·数据库·oceanbase
Themberfue4 小时前
Redis ⑦-set | Zset
java·开发语言·数据库·redis·sql·缓存
__lost5 小时前
MATLAB画出3d的常见复杂有机分子和矿物的分子结构
开发语言·人工智能·matlab·化学·分子结构
夜夜敲码6 小时前
C语言教程(十八):C 语言共用体详解
c语言·开发语言
大学生亨亨6 小时前
go语言八股文(五)
开发语言·笔记·golang
raoxiaoya6 小时前
同时安装多个版本的golang
开发语言·后端·golang