跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Java 多线程并发修改集合引发 ConcurrentModificationException 解析与方案

Java 多线程并发修改集合常引发 ConcurrentModificationException。该异常由迭代器的 fail-fast 机制触发,当检测到集合结构在遍历中被修改且未获保护时抛出。常见于 ArrayList 遍历时直接增删元素,或多线程共享同一实例。解决需选用线程安全容器如 CopyOnWriteArrayList 或 ConcurrentHashMap,或使用 Collections.synchronizedList 配合显式同步。理解底层 modCount 校验逻辑能有效规避此类运行时错误,保障并发稳定性。

Qiny01发布于 2024/4/12更新于 2026/10/685 浏览
Java 多线程并发修改集合引发 ConcurrentModificationException 解析与方案

问题现象

在日志里看到这样的堆栈:

java.util.ConcurrentModificationException: null at java.util.ArrayList.forEach(ArrayList.java:1260) at com.xxx.queryAllMyScenarioCardDetailInfo(ScenarioCardMyCustomServiceImpl.java:97)

这其实是 Java 集合框架里的经典坑。ArrayList 的迭代器是'快速失败'(fail-fast)的。它内部维护了一个 modCount 变量,记录集合被修改的次数。当你开始遍历时,它会记下当前的 modCount 值作为预期值。如果在遍历过程中,集合的结构发生了改变(比如 add 或 remove),modCount 就会增加。下一次调用 next() 时,迭代器发现当前 modCount 不等于预期的值,就会直接抛出这个异常。

虽然文档里说这是单线程下遍历修改也会触发,但在多线程环境下,如果多个线程同时操作同一个非线程安全的集合,这种竞争条件更容易发生,而且更难复现。

怎么解决

别慌,有几种成熟的方案。

  1. 用线程安全的集合类。如果是读多写少,CopyOnWriteArrayList 是个好选择。它每次修改都复制新数组,保证迭代时的数据一致性。
  2. 如果是 Map,直接用 ConcurrentHashMap。
  3. 如果必须用普通 List,可以用 Collections.synchronizedList 包装,但要注意迭代时要手动加锁。
  4. 或者在遍历前把数据拷贝一份,只遍历副本,原集合随便改。

代码示例

下面展示一下错误的写法,以及推荐的修正方式。

// 错误示范:遍历中直接修改
List<String> list = new ArrayList<>();
// ... 填充数据
for (String s : list) {
    if (shouldRemove(s)) {
        list.remove(s); // 这里会抛异常
    }
}

修正后的写法:

// 推荐:使用 Iterator 移除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (shouldRemove(s)) {
        it.remove(); // 安全移除
    }
}

如果是多线程环境,建议直接换容器:

List<String> safeList = new CopyOnWriteArrayList<>();

这样就能彻底避开这个问题。

目录

  1. 问题现象
  2. 怎么解决
  3. 代码示例

更多推荐文章

查看全部
  • LeetCode 141 环形链表判断:哈希表与快慢指针解法
  • Yii 框架核心系统组件详解:日志、路由与缓存
  • Mac 下基于 Neo4j 与 py2neo 的知识图谱搭建步骤详解
  • CloudBeaver 快速搭建指南:Web 数据库管理平台部署
  • 前端 PWA:构建离线可用与可安装的 Web 应用
  • OpenClaw 框架 30+ 真实场景深度解析
  • Clang Power Tools 实现 C++ 静态分析与 Visual Studio 深度集成
  • AI 赋能专利翻译,妙算翻译大模型亮相国际论坛
  • 基于 Spring Boot 的教室信息管理系统设计与实现
  • Kafka 常用命令行工具实战指南
  • OpenAI 自主推理与动态知识图谱四大技术突破解析
  • Rust 异步 API 设计与 Axum 框架实战
  • 电商策略自动化系统:结合数据采集与 AI 的分析实践
  • OpenCode 踩坑记:GitHub Copilot 按次计费与账单暴涨原因分析
  • SG 函数详解:博弈论通用解法与实战
  • 昇腾 NPU 部署 Llama 2 模型的性能测试与优化实践
  • C++ 虚函数与纯虚函数:深入理解多态机制
  • Spring Boot 自定义注解实战:5 个高频案例详解
  • 利用 AI 编程助手 7 天完成 Spring Boot 电商后台系统开发
  • OpenClaw(龙虾)掀起 AI 智能体革命,百度腾讯布局加速

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online