java.util.ConcurrentModificationException异常之阿里巴巴Java编程规范终极版

最近研读了阿里巴巴Java编程规范,通过学习前辈踩过的坑,发现自己和编程规范的要求大部分是相同的,这个编程规范应该得到推广,好的编程习惯从一个好的命名开始

看到以下的这条规范,自己试验后发现确实以前没有主意到这个细节,于是自己开始了详细的分析,折腾了好久好久,天都黑了

package com.copycat.test04;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class AlibabaJavaCodeGuidLine
{
    public static void test01()
    {
        List<String>list=new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        list.add("6");
        list.add("7");
        /*
        for(String item : list)
        {
            System.out.println("++++++++"+item);
            if("6".equals(item))         //把这里的"1"换成"2","3","4","5","6","7"后再执行
            {
                list.remove(item);
                //break;                 //如果加上break后则所有的执行都是正确的
                System.out.println("remove the "+item);
            }
        }*/
        Iterator<String> iterator = list.iterator();   //为了更好的跟踪程序的运行过程
        while(iterator.hasNext())                      //我将上面的增强for循环改为这里的iterator来实现,本质和
        {                                              //上面的循环是一样的 
            String string=iterator.next();             //可以发现remove被执行后modCount和expectedModCount的值不一样
            if("6".equals(string))                     //导致next()函数中的checkForComodification()函数返回false,抛出异常
            {                                          //这时后程序抛出java.util.ConcurrentModificationException,然后终止程序
                list.remove(string);
            }
        }
        for(String it:list)
        {
            System.out.println(it);
        }
    }      
    public static void main(String[] args)
    {
        test01();
    }
}

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.copycat.test04.AlibabaJavaCodeGuidLine.test01(AlibabaJavaCodeGuidLine.java:17)
at com.copycat.test04.AlibabaJavaCodeGuidLine.main(AlibabaJavaCodeGuidLine.java:34)

通过抛出的异常分析是并发修改里面的问题,于是要分析一下源代码里的详细细节

ArrayList的源码里面有一个内部类是迭代器

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();
        }
    }

看一下有几个重要的成员变量

cursor:表示下一个要访问的元素的索引,从next()方法的具体实现就可看出
lastRet:表示上一个访问的元素的索引
expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount。
modCount是AbstractList类中的一个成员变量

给出程序的过程图解,请内心观察,插入7个元素后的modCount和expectedModCount的值是一样的


然后一直取元素做判断,直到remove()被执行,这个过程主意黄色的变化






到这时要执行remove操作,注意modCount和size的变化


这时hasNext(){ return cursor!=size()}返回false不再执行了,造成后面的元素不再判断了,因此只有倒数第二个元素("6".equal())去比较的时候才会运行正常(实际是巧合),

其它元素去比较都会在Next()函数中抛出异常,原因是cursor和size的值不同返回true,然后遇到next()中的checkForComodification()而抛出异常

总结:不要在foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁

Iterator中expectedModCount = modCount;避免了在next()中checkForComdification()抛出异常

猜你喜欢

转载自blog.csdn.net/qq_28816195/article/details/78433544