JAVA遍历集合并修改集合抛出异常ConcurrentModificationException

前言:

遍历集合根据某个条件对集合内容进行修改,这是一个非常常用的情景,但在实际开发中有时会抛出ConcurrentModificationException有时候又不会。这里终结一下。

例子(一):

public class ArrayListTest {
    public static void main(String[] args) {
        List<Integer> listA=new ArrayList<>();
        listA.add(1);
        listA.add(2);
        listA.add(3);
        listA.add(4);
        listA.add(5);
        listA.add(6);
        removeThing01(listA,3);
    }
    public static void removeThing01(List<Integer> list,Integer value){
        for (Integer integer : list) {
            if (integer.equals(value)){
                list.remove(value);
            }
        }
    }
}

抛出异常:
在这里插入图片描述
这是在单线程的情况下,抛出ConcurrentModificationException异常,那么问题到底出在哪里呢?
这里就需要提一下Java的快速失败和安全失败
一:快速失败(fail—fast)

在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。

场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改或者迭代过程中被修改。

二:安全失败(fail—safe)

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

原理:
迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

在next方法执行的时候,会执行checkForComodification()方法在这里插入图片描述在这里插入图片描述
所以刚刚的例子我们的问题是在迭代过程中修改了集合,导致了modCount不等于expectedModCount。所以这里的解决方案是:改为迭代器方式来修改集合。

  public static void removeThing02(List<Integer> list,Integer value){
        Iterator<Integer> iterator=list.iterator();
        while (iterator.hasNext()){
            Integer integer=iterator.next();
            if (integer.equals(value)){
                iterator.remove();
            }
        }
    }

原理:每次通过迭代器来remove或者add都会修改expectedModCount的值。
在这里插入图片描述

例子(二):

public class ArrayListTest {
    public static void main(String[] args) {
        List<Integer> listA=new ArrayList<>();
        listA.add(1);
        listA.add(2);
        listA.add(3);
        listA.add(4);
        listA.add(5);
        listA.add(6);
        new Thread(() -> {
            try {
                removeThing02(listA,3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                removeThing02(listA, 4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

    }
    public static void removeThing02(List<Integer> list,Integer value) throws InterruptedException {
        Iterator<Integer> iterator=list.iterator();
        while (iterator.hasNext()){
            Integer integer=iterator.next();
            Thread.sleep(1000);
            if (integer.equals(value)){
                iterator.remove();
            }
        }
    }
}

在这里插入图片描述
问题出在哪?很明显是因为Iterator的remove,next方法都不是线程安全的,所以还是有机会触发modCount变量不等于expectedmodCount值抛出异常。
所以这里的解决方案是:
1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;
2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。
3)使用并发容器ConcurrentHashMap代替hashMap

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值