Java中 ConcurrentModificationException 在单线程代码逻辑错误时的排查与修复

星伟姑娘_7103

星伟姑娘_7103

2026-07-20

376人浏览

原创

concurrentmodificationexception在单线程中出现并非并发问题,而是遍历集合时直接修改结构违反fail-fast机制;其根本原因是modcount与expectedmodcount不一致,正确做法包括使用iterator.remove()、removeif()或倒序索引删除。

java中 concurrentmodificationexception 在单线程代码逻辑错误时的排查与修复

ConcurrentModificationException 在单线程中出现,根本不是“并发”问题,而是代码逻辑违反了集合的遍历-修改契约。它本质是 Java 集合的 fail-fast(快速失败)机制在报警:你正在一边读、一边偷偷改结构,而集合不允许这样操作。

为什么单线程也会抛这个异常?

ArrayList、HashMap 等非线程安全集合内部维护一个 modCount 计数器,记录结构修改次数(如 add/remove/clear)。当你调用 iterator() 或进入 for-each 循环时,迭代器会把当时的 modCount 值存为 expectedModCount。后续每次调用 next() 或 hasNext(),都会检查两者是否一致——一旦发现不等(比如你在循环里调了 list.remove()),立刻抛出异常。

这不是多线程竞争,而是同一段代码里,**遍历动作和结构修改动作没协调好**。

java-enterprise
java-enterprise

Java企业开发专家,专注于Spring Boot、微服务架构和JVM优化。适用于:Java语言掌握、

下载

典型错误写法与对应修复方式

以下都是单线程常见“踩坑”模式,附带正确解法:

  • 错:for-each 中直接 remove
    for (String s : list) { if (s.equals("xxx")) list.remove(s); }
    → 这会触发 fail-fast,因为 for-each 底层就是 Iterator,但你绕过它去动集合。
  • 错:Iterator 遍历时调集合的 remove
    Iterator<string> it = list.iterator(); while(it.hasNext()) { String s = it.next(); if (s.equals("xxx")) list.remove(s); }</string>
    → 同样破坏了迭代器预期状态。
  • 对:用 Iterator 自己的 remove 方法
    Iterator<string> it = list.iterator(); while(it.hasNext()) { String s = it.next(); if (s.equals("xxx")) it.remove(); }</string>
    → 这个 remove() 是安全的:它会同步更新 expectedModCount,并校验 lastRet 合法性。
  • 对:用 Collection.removeIf(Java 8+)
    list.removeIf(s -> s.equals("xxx"));
    → 内部封装了安全遍历逻辑,语义清晰,推荐用于简单条件删除。
  • 对:倒序 for 循环 + 索引 remove
    for (int i = list.size() - 1; i >= 0; i--) { if (list.get(i).equals("xxx")) list.remove(i); }
    → 避开正向遍历时索引偏移问题,且不依赖迭代器,无 fail-fast 检查。

排查技巧:一眼定位问题位置

异常堆栈通常指向 iterator.next() 或 hasNext() 调用处,但真正的问题在它之前的某次 remove()、add() 或 clear()。你可以:

  • 搜索当前方法内所有 .remove(、.add(、.clear() 调用;
  • 重点检查它们是否出现在 for-each、while(it.hasNext()) 或 stream().forEach() 等遍历结构内部;
  • 用 IDE 的 “Find Usages” 功能,追踪该集合变量的所有修改点,看是否有隐蔽的修改逻辑(比如被其他方法间接调用)。

额外提醒:别被名字误导

虽然叫 ConcurrentModificationException,但它在单线程下高频出现。不要一看到就怀疑线程安全问题——先确认是不是自己写的循环里动了集合。如果是多线程场景,那才需要考虑 CopyOnWriteArrayList、ConcurrentHashMap 或加锁等方案。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

10097

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

7242

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6372

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1104

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

908

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1336

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2669

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

19991

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1175

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习