美文网首页
java.util.ConcurrentModification

java.util.ConcurrentModification

作者: zycisbg | 来源:发表于2018-02-06 17:40 被阅读0次

    先看下边一段代码。

        @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的Fail-fast机制是什么实现的。
      在HashMap,ArrayList,LinkedList源码中都有modCount这个全局变量。在add/put,remove,clear等对集合内部结构发生变化的时候,都会改变modCount这个变量,modCount 初始为0,可以说modCount记录了该集合的改变次数。(前边三篇关于集合的文章并没有对modCount进行解释,在这里解释一下)

    下边通过源码看一下如何抛出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,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

    相关文章

      网友评论

          本文标题:java.util.ConcurrentModification

          本文链接:https://www.haomeiwen.com/subject/bqngzxtx.html