欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅谈fail-fast机制

程序员文章站 2022-05-29 12:55:30
fail-fast机制即为快速失败机制,个人认为是一种防护措施,在集合结构发生改变的时候,使尽全力抛出ConcurrentModificationException,所以该机制大部分用途都是用来检测Bug的; 下面的代码可以引发fail-fast 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个重要属性;

浅谈fail-fast机制

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