• 什么是快速失败(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 值,是的话就返回遍历;否则抛出异常,终止遍历。

        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();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    二、安全失败(fail-safe)

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

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

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

    三、测试代码:

    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);
     
        }
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    安全失败机制的集合容器中的Itr源码
    在集合采取该方法进行遍历时,

            snapshot: 数组的快照
    
            cursor: 调用next方法时,返回的元素的索引值
           
    
    • 1
    • 2
    • 3
    • 4

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

        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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
  • 相关阅读:
    间隔一分钟按某个按键一次——VBS脚本
    C++学习day2
    74. 搜索二维矩阵
    java毕业生设计成绩分析系统计算机源码+系统+mysql+调试部署+lw
    gateway整合sentinel限流
    HCIE Routing&Switching之MPLS基础理论
    Java 微信关注/取消关注事件
    对el-table表格的表头操作
    Integer 缓存机制
    vue-element-admin总结(全程复制不会剁手吧你!)
  • 原文地址:https://blog.csdn.net/qq_43070052/article/details/132725034