栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Java踩坑】01ArrayList中的remove操作导致的ConcurrentModificationException报错 源码解析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Java踩坑】01ArrayList中的remove操作导致的ConcurrentModificationException报错 源码解析

前要:在看一道题,看到一题ArrayList的remove,问如下是否会报错

	public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        String reString = "张三";
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("小明");
         for (String n : list) {
              if (reString.equals(n)) {
                  list.remove(n);
              }
         }
      }

运行代码,报错如下:

我看了别人的文章,发现别人只是说modCount发生改变,但是具体是如何改变的就没有细说,感觉不够细致。

思考过程:
之前我看过解决的方案使用Iterator迭代器进行遍历。

public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        String reString = "张三";
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("小明");
//        报错:ConcurrentModificationException
//        for (String n : list) {
//            if (reString.equals(n)) {
//                list.remove(n);
//            }
//        }
        //iterator 正确写法
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().equals(reString)) {
                iterator.remove();
            }
        }
        for (String n:list) {
            System.out.println(n);
        }
    }

执行结果:执行结果和预期结果一致,正确

思考:想着都是使用了remove,有什么不同么?

抱着这样的思考打断点,查看ArrayList源码

首先看ForEach增强循环中的remove源码,如下:

ArrayList源码


此时modCount因为增加了remove操作,所以加一,变成了5

接下来,就是报错的关键了,因为ForEach语法糖中的next了,如下



因为modCount(修改次数:5次)和expectedModCount(预计修改的次数:4次)不相等,因为多了一次remove操作,所以直接报错。

但我看到这里,我在想,难道使用iterator迭代器遍历remove难道有什么不同么?
果然,在我一次次打断点查看后,发现关键因素是Iterator迭代器本身就是一个内部类,这里的remove是使用的内部类中到方法

Iterator源码:

Fast-Fail机制

通过这个问题,我就衍生出了Fast-Fail机制,其实该机制就是但线程a为了对非线程安全的数据进行遍历时,线程b对数据进行修改,则modCount发生改变,线程a再下一次遍历时会通过checkForComodification()方法检测,判断modCount更改,从而直接报错。

解决方案:
面对并发问题,并发Java中的Copy-On-Write容器可以很好的解决该问题

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。

大佬文章:
Fast-Fail机制

收获与总结:

  1. forEach语法糖是使用了hasNext()、next()方法
  2. 对于ArrayList进行数据的修改,需要直接使用Iterator迭代器进行修改
  3. 面对Fast-Fail机制的并发问题,可以使用CoryOnWrite容器,扩展CopyOnWrite特性
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/841070.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号