java.util.ConcurrentModification

2018-02-06  本文已影响0人  zycisbg

先看下边一段代码。

    @Test
    public void test(){
        List<String> list = new ArrayList();

        list.add("1");
        list.add("1");
        list.add("1");
        list.add("1");
        list.add("1");

        for (String s : list){
            list.add("2");
            System.out.println(s);
        }
    }

运行上边这一段代码会报一个java.util.ConcurrentModificationException的异常,。
为什么会报这样的异常,因为java的fail-fast机制

Fail-fast 机制是 Java 集合中的一种错误机制。 当一个线程对集合进行迭代,其他线程对该集合进行内部结构改变。或者该线程在迭代的时候又对其进行改变,就可能会产生 Fail-fast 事件。

下边通过源码看一下如何抛出java.util.ConcurrentModificationException这个异常的。

        //增强for循环遍历和迭代器遍历都需要用到 Iterator 中的next方法。
        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];
        }
        //检查modCount  是否和expectedModCount相等。
        //modCount我们知道每发生一次变化就会对其进行改变
        //而expectedModCount在创建时 就会被赋值
        //所以在遍历的时候,只要modCount发生改变,则就会抛出异常。
        int expectedModCount = modCount;


        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

注意事项

这里异常抛出条件是检测modCount != expectedModCount,如果集合内发生变化,然后恰好modCount = expectedModCount ,那么则不会抛出异常。因此不能依赖是否抛出异常来进行并发编程。这个异常仅供检测使用。

在java.util包下的集合基本上都是快速失败的。
在java.concurrent包下是安全失败的。

fail-safe 安全失败

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

原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。

缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

上一篇下一篇

猜你喜欢

热点阅读