ConcurrentModificationException(并发修改异常)原因分析

1.现象还原

出现在用foreach中用集合(如arrayList)进行元素的删减

public static void test2(){
        List<String> myList = new ArrayList<String>();
        myList.add( "1");
        myList.add( "2");
        myList.add( "3");
        myList.add( "4");
        myList.add( "5");
        for (String value : myList) {
            System. out.println( "List Value:" + value);
            if (value.equals( "2")) {
                // error
                myList.add(value);
                // error
                myList.remove(value);
            }
        }
    }

List Value:1
List Value:2
Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
	at com.example.demo.thread.multiThread.ConcurrentModification.test2(ConcurrentModification.java:51)
	at com.example.demo.thread.multiThread.ConcurrentModification.main(ConcurrentModification.java:24)

Process finished with exit code 1

2.异常原因

总结:用迭代器进行循环(iterator.next()),但是用list的实现(如arraylist)进行元素的操作(add || remove),导致Iterator.next()进行check时候,exceptedModCount != ModCount;(

1.iterator每次Next()都会检查两个数是否相等(checkForComodification),不等就抛出并发修改异常

2.ArrayList的每次Add或者remove都会进行ModCount++;

3.第一次初始化时,exceptedModCount =ModCount,当进行了一次arrarList.remove()后,modCount+1,但是exceptedModCount没有变,当进行下一次Itr.Next()时,检测checkForComodification (modCount != expectedModCount)==true 就会抛出异常

注:1.exceptedModCount 为Itr的变量,是期望被修改的次数, 初始时赋值为modCount ;

2.ModCount为ArrayList的父类AbstractList的变量,记录当前的ArrayList的操作次数;

解析:

前提:1.foreach的循环:底层采用的是迭代器Iterator(Itr,这个类是ArrayLIst实现的内部类),每次循环都会调用.next()访问下一个元素。

2.普通的for循环,使用的是索引去访问数组,不会出现这个异常。


这里的Itr是ArrayList的内部类,实现了Iterator用于遍历

3.源码分析

ArrayList的remove()

public boolean remove(Object o) {
        final Object[] es = elementData;
        final int size = this.size;
        int i = 0;
        found: {
            if (o == null) {
                for (; i < size; i++)
                    if (es[i] == null)
                        break found;
            } else {
                for (; i < size; i++)
                    if (o.equals(es[i]))
                        break found;
            }
            return false;
        }
        //
        fastRemove(es, i);
        return true;
    }
  private void fastRemove(Object[] es, int i) {
      //删除时会对modCount进行+1;
        modCount++;
        final int newSize;
        if ((newSize = size - 1) > i)
            System.arraycopy(es, i + 1, es, i, newSize - i);
        es[size = newSize] = null;
    }

ArrayList的add()

   public boolean add(E e) {
        //arrayList的add也会+1
        modCount++;
        add(e, elementData, size);
        return true;
    }

内部类Itr:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
............
    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
        //记录预期修改次数 初始化为AbstractList的modCount;
        int expectedModCount = modCount;

        // prevent creating a synthetic constructor
        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            //检查modCount 和 expectedModCount是否相等,不等抛出ConcurrentModificationException
            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];
        }
        ..........................
        }
   
      //检测方法
      final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
            
}

总结:

可以看到,调用ArrayList的remove或者Add都会对modCount进行+1的操作(操作的时候不会对expectedModCount做任何的操作)。当remove()或者add操作完进行下一次迭代(Itr.next())时,调用checkForComodification()就会发现两个值不等,就抛出异常。


避免:

1.使用普通for循环

2.使用stream对元素操作,如过滤返回新的集合

栗子:

List<String> newCollect = myList.stream().filter(item -> !StringUtils.isEmpty(item)).collect(Collectors.toList());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值