• 【Java异常】Exception in thread“main” java util ConcurrentModificationException的解决方案


    本文目录

    一、错误描述

    二、错误原因

    三、解决方案

    3.1 方案一

    3.2 方案二


    总结

    如果使用ArrayList集合时,遍历删除可以使用Iterator迭代器遍历,不能使用for(;;){}这种foreach格式的;
    如果使用CopyOnWriteArrayList集合时,遍历删除可以使用foreach遍历,不能使用Iterator迭代器进行删除;

    一、错误描述

    ArrayList是java开发时经常使用的一个类,又经常碰到需要对ArrayList循环删除元素的情况。这时候大家都不会使用foreach循环的方式来遍历List,因为它会抛java.util.ConcurrentModificationException异常。 Vector也同样会报异常。比如下面的代码:

    public class ConcurrentTest {
    
        public static void main(String[] args) {
    
            List<Person> list = new ArrayList<>();
    
            list.add(new Person("张三", 23));
            list.add(new Person("李四", 24));
            list.add(new Person("王五", 25));
            list.add(new Person("麻六", 26));
    
            list.forEach(person -> {
                if (person.getAge() == 24){
                    list.remove(person);
                }
            });
    
            System.out.println(list);
        }
    }

    抛出异常信息如下:

    Exception in thread "main" java.util.ConcurrentModificationException
    	at java.util.ArrayList.forEach(ArrayList.java:1252)
    	at com.uiotsoft.back.iotoperation.business.ConcurrentTest.main(ConcurrentTest.java:23)

    二、错误原因

    其实,基本上所有的集合类都会有一个叫做快速失败的校验机制,当一个集合在被多个线程修改并访问时,就会出现ConcurrentModificationException 校验机制。它的实现原理就是我们经常提到的modCount修改计数器。如果在读列表时,modCount发生变化则会抛出ConcurrentModificationException异常。这与线程同步是两码事,线程同步是为了保护集合中的数据不被脏读、脏写而设置的。

    首先java的foreach循环其实就是根据list对象创建一个Iterator迭代对象,用这个迭代对象来遍历list,相当于list对象中元素的遍历托管给了Iterator,你如果要对list进行增删操作,都必须经过Iterator。iterator创建的时候modCount被赋值给了expectedModCount,但是调用list的add和remove方法的时候不会同时自动增减expectedModCount,这样就导致两个count不相等,从而抛出异常。

    三、解决方案

    以下提供两种解决方案:

    3.1 方案一

    使用CopyOnWriteArrayList<>(),例如如下例子:

    public class ConcurrentTest {
    
        public static void main(String[] args) {
    
            List<Person> copyList = new CopyOnWriteArrayList<>();
    
            copyList.add(new Person("张三", 23));
            copyList.add(new Person("李四", 24));
            copyList.add(new Person("王五", 25));
            copyList.add(new Person("麻六", 26));
    
            copyList.forEach(person -> {
                if (person.getAge() == 25){
                    copyList.remove(person);
                }
            });
    
            System.out.println(copyList);
        }
    }

    结果正常:

    3.2 方案二

    使用增强for循环

    public class ConcurrentTest {
    
        public static void main(String[] args) {
    
            List<Person> list = new ArrayList<>();
    
            list.add(new Person("张三", 23));
            list.add(new Person("李四", 24));
            list.add(new Person("王五", 25));
            list.add(new Person("麻六", 26));
    
            for (Person person : list) {
                if (person.getAge() == 25){
                    list.remove(person);
                }
            }
    
            System.out.println(list);
        }
    }
    

    结果正常,请看方案1的结果。

     

    【参考资料】

    关于ArrayList与CopyOnWriteArrayList的遍历remove操作:https://blog.csdn.net/MrLi_IT/article/details/89186169

  • 相关阅读:
    关于TileBrush中Viewbox,Viewport以及Stretch,AlignmentX/Y的详细研究
    ListBox数据绑定无效
    WPF---Effect效果
    wpf 画刷的分类
    LinearGradientBrush,RadialGradientBrush的样式说明
    改变ListBoxItem选中的颜色
    自定义的 ListBoxItem 自适应ListBox的宽度
    WPF ListBox数据绑定
    ItemsPanelTemplate的用法
    svn报错Item is not readable svn解决方案
  • 原文地址:https://www.cnblogs.com/no8g/p/13415485.html
Copyright © 2020-2023  润新知