Java中ConcurrentModificationException异常处理

发布时间 - 2026-01-09 00:00:00    点击率:
ConcurrentModificationException是单线程下“边迭代边修改”触发的fail-fast机制,非多线程并发导致;底层通过modCount与expectedModCount比对检测结构变更,不一致则抛异常。

为什么遍历集合时修改会抛出ConcurrentModificationException

这个异常不是因为多线程并发导致的(虽然名字容易误导),而是ArrayListHashMap等非线程安全集合在单线程中「边迭代边修改」触发了快速失败(fail-fast)机制。底层靠modCount计数器检测结构变更:每次调用add()remove()等方法都会递增它,而迭代器在next()hasNext()时会校验当前modCount是否与初始化时一致,不一致就直接抛异常。

常见错误现象:

  • for-each循环遍历ArrayList,内部调用iterator(),然后在循环体里调用list.remove(obj)
  • Iterator遍历,但调用的是集合自身的remove()而非iterator.remove()
  • 多个线程共用同一个ArrayList,一个在读(遍历),一个在写(增删)

单线程下安全删除元素的正确方式

核心原则:必须通过迭代器自身提供的remove()方法来删除当前元素,它会同步更新expectedModCount,避免校验失败。

List list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
Iterator it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("b".equals(s)) {
        it.remove(); // ✅ 正确:调用迭代器的 remove()
    }
}

其他可行方案:

  • 收集待删元素,遍历结束后统一调用list.removeAll(toRemove)
  • 使用removeIf()(Java 8+):list.removeIf(s -> "b".equals(s))
  • 倒序索引遍历(仅适用于ArrayList等支持随机访问的集合):for (int i = list.size() - 1; i >= 0; i--) { if (needRemove(list.get(i))) list.remove(i); }

多线程环境下的替代集合选择

如果确实需要多线程并发读写且遍历时不报错,不能靠加锁硬扛(锁住整个遍历过程性能差、易死锁),应换用线程安全的集合实现:

  • CopyOnWriteArrayList:适合读多写少场景;遍历时操作的是快照,写操作会复制整个数组,Iterator不支持remove()
  • ConcurrentHashMap:迭代其keySet()values()entrySet()不会抛ConcurrentModificationException,但结果可能不反映实时状态(弱一致性)
  • BlockingQueue子类(如LinkedBlockingQueue):适用于生产者-消费者模型,提供线程安全的增删操作

注意:VectorHashtable虽是线程安全的,但它们的迭代器仍是fail-fast的,同样会抛该异常。

调试时如何快速定位问题源头

异常堆栈通常只显示在next()hasNext()处抛出,但真正引发modCount变化的位置往往在别处。关键看异常信息中的「at」行和上层调用链:

  • 检查堆栈中是否有remove()add()clear()等结构修改方法出现在迭代逻辑附近
  • 确认是否在Lambda表达式、Stream操作(如forEach)中隐式调用了集合修改方法
  • 留意匿名内部类或监听器回调里是否意外修改了外部集合

最容易被忽略的一点:即使你没显式写remove(),某些工具方法(比如Collections.synchronizedList()包装后返回的代理对象,其iterator()仍不支持并发修改)也会导致相同行为——它只是同步了单个方法,没解决迭代器一致性问题。


# java  # 工具  #   # ai  # stream  # 为什么 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 七夕网站制作视频,七夕大促活动怎么报名?  如何获取PHP WAP自助建站系统源码?  Laravel路由怎么定义_Laravel核心路由系统完全入门指南  Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】  手机网站制作平台,手机靓号代理商怎么制作属于自己的手机靓号网站?  如何在自有机房高效搭建专业网站?  Laravel的路由模型绑定怎么用_Laravel Route Model Binding简化控制器逻辑  Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】  Laravel如何实现事件和监听器?(Event & Listener实战)  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践  如何在建站之星绑定自定义域名?  JavaScript模板引擎Template.js使用详解  今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】  PHP正则匹配日期和时间(时间戳转换)的实例代码  简单实现jsp分页  php后缀怎么变mp4格式错误_修改扩展名提示格式不对怎么办【技巧】  高防服务器:AI智能防御DDoS攻击与数据安全保障  Swift中switch语句区间和元组模式匹配  如何在IIS中配置站点IP、端口及主机头?  Laravel怎么清理缓存_Laravel optimize clear命令详解  成都品牌网站制作公司,成都营业执照年报网上怎么办理?  Java垃圾回收器的方法和原理总结  大学网站设计制作软件有哪些,如何将网站制作成自己app?  Laravel如何使用模型观察者?(Observer代码示例)  如何快速上传自定义模板至建站之星?  Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧  HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】  如何在万网利用已有域名快速建站?  北京网页设计制作网站有哪些,继续教育自动播放怎么设置?  php结合redis实现高并发下的抢购、秒杀功能的实例  如何在云虚拟主机上快速搭建个人网站?  Laravel数据库迁移怎么用_Laravel Migration管理数据库结构的正确姿势  Windows10怎样连接蓝牙设备_Windows10蓝牙连接步骤【教程】  Python制作简易注册登录系统  千问怎样用提示词获取健康建议_千问健康类提示词注意事项【指南】  Java遍历集合的三种方式  动图在线制作网站有哪些,滑动动图图集怎么做?  如何彻底卸载建站之星软件?  在centOS 7安装mysql 5.7的详细教程  PythonWeb开发入门教程_Flask快速构建Web应用  javascript基本数据类型及类型检测常用方法小结  Laravel如何使用Service Provider注册服务_Laravel服务提供者配置与加载  EditPlus中的正则表达式 实战(1)  如何基于云服务器快速搭建个人网站?  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  Laravel如何实现API资源集合?(Resource Collection教程)  如何快速搭建安全的FTP站点?  利用JavaScript实现拖拽改变元素大小  微信h5制作网站有哪些,免费微信H5页面制作工具?