• 浅谈fail-fast机制


    fail-fast机制即为快速失败机制,个人认为是一种防护措施,在集合结构发生改变的时候,使尽全力抛出ConcurrentModificationException,所以该机制大部分用途都是用来检测Bug的;

    下面的代码可以引发fail-fast

     1     public static void main(String[] args) {
     2         List<String> list = new ArrayList<>();
     3         for (int i = 0 ; i < 10 ; i++ ) {
     4             list.add(i + "");
     5         }
     6         Iterator<String> iterator = list.iterator();
     7         int i = 0 ;
     8         while(iterator.hasNext()) {
     9             if (i == 3) {
    10                 list.remove(3);
    11                 //list.add("11");   添加元素同样会引发
    12             }
    13             System.out.println(iterator.next());
    14             i ++;
    15         }
    16     }

    fail-fast原理

    每个集合都会实现可遍历的接口,以上述代码为例,集合调用iterator();方法的时候,其实是返回了一个new Itr();

        /**
         * Returns an iterator over the elements in this list in proper sequence.
         *
         * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
         *
         * @return an iterator over the elements in this list in proper sequence
         */
        public Iterator<E> iterator() {
            return new Itr();
        }

    以下是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;
    
            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();
            }
        }

    Itr有3个重要属性;

    cursor是指集合遍历过程中的即将遍历的元素的索引

    lastRet是cursor -1,默认为-1,即不存在上一个时,为-1,它主要用于记录刚刚遍历过的元素的索引。

    expectedModCount它初始值就为ArrayList中的modCount(modCount是抽象类AbstractList中的变量,默认为0,而ArrayList 继承了AbstractList ,所以也有这个变量,modCount用于记录集合操作过程中作的修改次数)

    由源码可以看出,该异常就是在调用next()的时候引发的,而调用next()方法的时候会先调用checkForComodification(),该方法判断expectedModCount与modCount是否相等,如果不等则抛异常了

    那么问题就来了,初始化的时候expectedModCount就被赋值为modCount,而且源码当中就一直没有改变过,所以肯定是modCount的值变了

    arrayList继承了abstractList,abstractList有modCount属性,通过以下源码我们可以看到,当ArrayList调用add、remove方法,modCount++

        /**
         * Inserts the specified element at the specified position in this
         * list. Shifts the element currently at that position (if any) and
         * any subsequent elements to the right (adds one to their indices).
         *
         * @param index index at which the specified element is to be inserted
         * @param element element to be inserted
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
    
        /**
         * Removes the element at the specified position in this list.
         * Shifts any subsequent elements to the left (subtracts one from their
         * indices).
         *
         * @param index the index of the element to be removed
         * @return the element that was removed from the list
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;
        }

    所以由此可见,对集合的操作中若modCount发生了改变,则会引发fail-fast机制;同时可以看出如果想要移除集合某元素,可以使用迭代器的remove方法,则不会引发fail-fast;

    发表该文章也参考了许多另一片文章的内容,详情地址:https://blog.csdn.net/zymx14/article/details/78394464

  • 相关阅读:
    openLDAP 2
    OPEN LDAP
    ORA-00604: 递归 SQL 级别 1 出现错误 ORA-01000: 超出打开游标的最大数
    linux常用命令
    EMC存储同时分配空间到两台服务器路径不一致-双机盘符不一致
    新年SO交期更新——FP_SO2SAP
    表有主外键约束时的delete 方法 2008
    mix_alternates_for_parent: TRUE
    javaweb:关于HttpServletRequest介绍 (转)
    Tomcat 调优的技巧 (转)
  • 原文地址:https://www.cnblogs.com/pengx/p/9576613.html
Copyright © 2020-2023  润新知