ConcurrentModificationException并发修改异常源码分析

一、引言

我们先来看下面这个程序(修改集合有增添和删除两种,下面是以增添进行分析,删除同理)

public class ListDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("hello");
        list.add("world");
        list.add("java");

        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            if ("world".equals(s)) {
                list.add("javaee");
            }
        }
    }
}

运行结果如下

Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
	at com.itheima_02.ListDemo.main(ListDemo.java:22)

当我们运行上面程序时,结果会报错ConcurrentModificationException(并发修改异常)


ConcurrentModificationException:当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。

二、异常源码分析

 1. 根据终端显示的异常信息,可以看到是第22行String s = it.next();的next()方法报错,而这个next()方法是集合list通过调用iterator()方法产生对应迭代器的方法。我们先跟进一下Iterator<String> it = list.iterator();的iterator()方法。

public Iterator<E> iterator() {
        return new Itr();
    }

 2. 可以看到iterator()方法返回一个Iterator实现类对象Itr,再跟进一下Itr类

private class Itr implements Iterator<E> {
        int expectedModCount = modCount;

        @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];
        }

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

 3. 从这个Itr内部类可以找到next()方法,执行next()方法的第一条语句是
checkForComodification();我们再跟进一下这个方法

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

 4. 可以看到这个方法做一个判断,判断modCount和expectedModCount是否相等如果不等就会抛出ConcurrentModificationException异常。

modCount:实际集合修改次数
expectedModCount:预期修改次数

 5. 我们看2中的Itr类的第一条语句int expectedModCount = modCount;,这条语句保证了modCount与expectedModCount相等,但是当我们调用list的add()方法时就会修改modCount的值,使得二者不相等,再调用next()进行检查时就会抛出并发修改异常。下面看一下add()方法代码,可以看到在调用add()方法时,第一条语句会让modCount++操作,使得modCount与expectedModCount不相等。

public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }

三、集合的三种遍历方式

并发修改异常通常发生在遍历集合时并对其进行修改,遍历集合有三种方式。


☀️ 第一种:普通for循环

【注释】普通for循环边遍历边修改集合不会出现并发修改异常

public class Test implements I {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("javaee");
        list.add(1, "python");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
    }
}

☀️ 第二种:增强for循环(推荐使用遍历集合的方式

【注释】增强for循环底层用的也是集合迭代器进行遍历,所以增强for循环和迭代器循环都会触发并发修改异常。

public class Test implements I {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("javaee");
        list.add(1, "python");
        for (String s : list) {
            System.out.println(s);
        }
    }
}

☀️ 第三种:创建集合迭代器遍历

public class Test implements I {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        list.add("javaee");
        list.add(1, "python");

        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值