当前位置: 移动技术网 > IT编程>开发语言>Java > 浅谈fail-fast机制

浅谈fail-fast机制

2018年09月03日  | 移动技术网IT编程  | 我要评论

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网