You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ArrayList出现ConcurrentModificationException并发修改异常根因排查求助

问题根因确认

你提出的异常触发逻辑完全成立,问题核心出在静态变量共享和类初始化时机两个点上:

1. 静态变量不存在子类独立副本

protected修饰的静态变量属于父类SomeObj本身的类级属性,只要子类没有显式声明同名的allFields静态变量,直接调用allFields操作的就是父类的同一个全局列表实例,不会生成子类独立副本。你代码里ChildSomeObj没有重新定义自己的allFields,所以它的静态代码块执行allFields.add("additional_field")时,修改的就是SomeObj中唯一的那个ArrayList实例。

2. 类初始化时机符合并发修改条件

JVM的类静态初始化只会在类第一次被主动使用时触发(比如首次创建子类实例、访问子类静态属性/方法、反射调用子类等)。如果其他线程刚好在你迭代SomeObj.getAllFields()返回的列表时,首次触发了ChildSomeObj的类加载,就会执行它的静态代码块修改allFields,迭代器检测到列表修改次数(modCount)和初始化迭代器时记录的次数不一致,就会抛出你日志中的ConcurrentModificationException,和你给出的报错栈完全匹配。

修复方案

可以根据你的业务需求选择以下任意一种方案:

  • 方案1:从根源禁止修改共享列表,在SomeObj的静态代码块初始化完成allFields后,将其封装为不可变列表:
    static {
        List<String> temp = new ArrayList<>();
        temp.add("foo");
        temp.add("bar");
        temp.add("baz");
        allFields = Collections.unmodifiableList(temp);
    }
    
    该方案会让子类修改allFields的操作直接抛出异常,第一时间暴露非法修改的代码位置,适合不需要动态扩展字段的场景。
  • 方案2:每次返回列表副本,修改getAllFields()方法,返回原列表的拷贝,避免外部迭代操作受原列表修改影响:
    public static List<String> getAllFields() {
        return new ArrayList<>(allFields);
    }
    
  • 方案3:使用线程安全的列表实现,将allFields的初始化改为CopyOnWriteArrayList,它的迭代器基于快照实现,修改操作不会触发并发修改异常:
    static {
        allFields = new CopyOnWriteArrayList<>();
        // 后续add操作不变
    }
    
  • 方案4:提前触发子类初始化,在应用启动阶段就主动加载所有继承SomeObj的子类,确保静态代码块在业务逻辑运行前就执行完成,避免运行时并发修改。

内容的提问来源于stack exchange,提问作者havan79476

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 22:27:04