美文网首页Android知识
Java ConcurrentModificationExcep

Java ConcurrentModificationExcep

作者: zhangxiao | 来源:发表于2017-05-02 20:10 被阅读0次

    概述

    ConcurrentModificationException 可以直接从字面理解:同时更改异常。也就是说,在对某一数据执行某一操作的时候,同时更改了数据,造成错误。

    举个栗子

    在遍历一个List同时remove其中的某个item

    package com.example;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class MyClass {
        static List<String> mList = new ArrayList<>();
    
        public static void main(String[] args) {
            MyClass myClass = new MyClass();
            myClass.initData();
            //forEach遍历同时remove某个item
            for (String s : mList) {
                if ("aaa".equals(s)) {
                    mList.remove(s);
                }
            }
            myClass.iterator(mList);
            //模拟forEach遍历同时remove某个item
            Iterator<String> iterator = mList.iterator();
            while (iterator.hasNext()) {
                String s = iterator.next();
                if ("aaa".equals(s)) {
                    mList.remove(s);
                }
            }
            myClass.iterator(mList);
            //iterator遍历同时remove某个item
            Iterator<String> iterator1 = mList.iterator();
            while (iterator1.hasNext()) {
                String s = iterator1.next();
                if ("aaa".equals(s)) {
                    iterator1.remove();
                }
            }
            myClass.iterator(mList);
            //for遍历同时remove某个item
            for (int i = 0; i < mList.size(); i++) {
                String s = mList.get(i);
                if ("aaa".equals(s)) {
                    mList.remove(s);
                }
            }
            myClass.iterator(mList);
        }
    
        public void initData() {
            mList.add("aaa");
            mList.add("bbb");
            mList.add("ccc");
            mList.add("ddd");
        }
    
        public void iterator(List<String> list) {
            for (String s : list) {
                System.out.println(s+"  ");
            }
        }
    }
    
    

    分别用这四种实现逻辑去遍历(注释其它的三种实现),打印结果依次是:

    image.png image.png image.png image.png

    分析问题

    从打印的结果我们可以看到前面两种实现会报ConcurrentModificationException ,其实这两种实现本质是一种,第一种forEach的方式其实是在第二种iterator上包了一层语法糖。第三种实现是iterator的正确实现方式,对比一下第二种,区别只是在mList.remove和iterator.remove,OK,这样就成功的缩小了问题范围。接下来,直接看源码:

    /**
         * 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();
        }
    
        /**
         * An optimized version of AbstractList.Itr
         */
        private class Itr implements Iterator<E> {    
            @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();
                }
            }
        }
    

    重点看remove方法,ArrayList.this.remove(lastRet)之后,有一个expectedModCount = modCount。再看next()方法的checkForComodification():

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

    问题就出在这里,iterator的remove方法有一个同步计数的逻辑。

    总结

    在遍历List的时候,如果对List数据有改动,不应用forEach的遍历方式,可以用for或者iterator来替代。

    相关文章

      网友评论

        本文标题:Java ConcurrentModificationExcep

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