当前位置: 首页 > news >正文

Java的ConcurrentModificationException异常介绍和解决方案

关于ConcurrentModificationException 异常介绍

在一个线程遍历集合的时候(如ArrayList,HashMap),结构被修改(如remove, add),就会抛出这个异常。

是一个fail fast机制,为了在并发修改的时候发现问题,而不是返回错误数据。

出现的原因

源于ArrayList中的modCount字段

protected transient int modCount = 0;

这个字段的作用是记录结构的修改次数

还有Iterator中的expectedModCount字段,如果expectedModCount不等于modCount,就会抛出CME

private class Itr implements Iterator<E> {int cursor;       // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;public void remove() {try {ArrayList.this.remove(lastRet);cursor = lastRet;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}
}
  • modCount:记录集合被结构修改的次数(add、remove、clear等)
  • expectedModCount:迭代器期望的修改次数

在代码中出现CME的情况

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");for (String s : list) {list.remove(s); // 抛 ConcurrentModificationException
}

以上代码的链表就会发生结构变化,究其根本就是ArrayList修改了但是没有同步到Iterator迭代器,导致modCount != expectedCount从而抛出CME

正确的写法

1)使用Iterator.remove()方法

Iterator<String> it = list.iterator();
while (it.hasNext()) {String s = it.next();if (s.equals("A")) {it.remove();  // ✔ 不会抛 CME}
}

原因是:

public void remove() {// ...code...try {ArrayList.this.remove(lastRet); // 调用集合的removecursor = lastRet;lastRet = -1;expectedModCount = modCount; // 关键:同步更新!} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}
}

2)使用CopyOnWriteArrayList

CopyOnWriteArrayList适合读多写少

List<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");for (String s : list) {list.remove(s);  // ✔ 完全没问题
}

因为CopyOnWriteArrayList修改时会创建新的数组,读数据还是遍历旧数组,不会发生CME

3)使用 for 循环倒序遍历

for (int i = list.size() - 1; i >= 0; i--) {list.remove(i); // ✔ 不会 CME
}

4)使用removeIf()

list.removeIf(s -> s.equals("A"));

多线程下出现CME的情况

List<Integer> list = new ArrayList<>();new Thread(() -> {list.add(1);
}).start();new Thread(() -> {for (Integer i : list) {System.out.println(i); // ❌ 可能 CME}
}).start();

解决方案:

使用并发集合:

  1. ConcurrentHashMap
  2. CopyOnWriteArrayList
  3. ConcurrentLinkedQueue
  4. ConcurrentSkipListMap

Fail-fast是什么

指的是程序在运行代码的过程中,如果遇到错误或者异常状态,立即抛出异常停止运行,避免在后续的操作中引发更严重的数据不一致

http://www.jsqmd.com/news/48746/

相关文章:

  • 深入理解 Dart 中的 const 与 final:编译时常量与运行时常量
  • python: 缩放图片
  • java和python做出什么
  • java和linux
  • 湖南工程学院 学科实践与创新协会电气部 幕后揭示
  • KEYDIY PAK06-ZB Phone As Key: Replace Your Car Key with Your Smartphone for European/American Cars
  • 湖南工程学院 学科实践与创新协会电气部 新生选拔赛
  • It Calculus
  • 20232412 2024-2025-1 《网络与系统攻防技术》实验六实验报告
  • 20232309 2025-2026-1 《网络与系统攻防技术》实验六实验报告
  • 2025 ICPC 西安区域赛 VP
  • K8s学习笔记(二十二) 网络组件 Flannel与Calico - 详解
  • 完整教程:人脸识别4-Windows下基于MSVC编译SeetaFace6
  • CF1483D-Useful Edges
  • Paddle-CLS图像分类_环境安装
  • 2025年11月短视频运营公司最新TOP5推荐:业绩增长与效率筛选标准
  • 实用指南:【10】MFC入门到精通——MFC 创建向导对话框、属性页类、属性表类、代码
  • 2025-09-10-Wed-T-Kubernetes
  • 一文入门 Dify平台的插件开发
  • 20232326 2025-2026-1 《网络与系统攻防技术》实验六实验报告
  • 2025年11月小程序开发公司TOP5评测:功能落地与适配筛选标准,西南地区企业选择指南
  • 2025年11月云南数字人供应商最新TOP5推荐:精细建模优质选择
  • 第二讲下梯度下降算法
  • Java云计算技术怎样应对故障
  • 2025-08-02-Sat-T-RabbitMQ
  • Nand2Tetris 笔记
  • 审美积累暗色UI设计超越美学的用户体验
  • 具有超高峰值抑制比和低功耗的全光可调谐微波滤波器
  • 11.23
  • 实用指南:F-INR: Functional Tensor Decomposition for Implicit Neural Representations