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

java.util.ConcurrentModificationException 并发修改异常

目录

异常代码片段

详细说明

解决方案

使用迭代器进行遍历

使用临时集合存储结果


异常代码片段

if (ObjectUtil.isNotEmpty(candidateUsers)) {candidateUsers = candidateUsers.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());for (String candidateUser : candidateUsers) {if (ObjectUtil.isNotEmpty(candidateUser) && candidateUser.contains("${")) {try {String expressionValue = expressionService.getStrValue(extendHisprocinst.getProcessInstanceId(), candidateUser);candidateUsers.add(expressionValue);} catch (Exception e) {log.error("解析人员报错,原因:{}", ExceptionUtil.stacktraceToString(e));}}}
}

这段代码中出现ConcurrentModificationException异常的原因是,在遍历candidateUsers集合的同时对其进行了修改。

具体来说,是在遍历过程中通过candidateUsers.add(expressionValue);向集合中添加元素,这会导致ConcurrentModificationException异常。

详细说明

详细解释

1.集合的遍历机制:

  • 当你使用for-each循环遍历集合时,实际上是在使用迭代器(Iterator)来遍历集合中的元素。

  • 迭代器在遍历集合时会维护一个内部计数器,以跟踪当前遍历的位置。

  • 每次调用next()方法时,迭代器会移动到下一个位置,并返回当前位置的元素。

2.并发修改检测:

  • Java集合框架中的许多集合类(如ArrayList)在遍历期间会检查集合是否被修改过。

  • 这种检查是通过一个称为modCount的字段来实现的,每当集合被修改时(例如添加或删除元素),modCount值就会增加。

  • 迭代器有一个expectedModCount字段,它在迭代器创建时被初始化为集合的modCount值。

  • 在每次调用next()方法时,迭代器会检查expectedModCount是否等于集合的当前modCount值。

  • 如果expectedModCount与当前modCount不匹配,说明集合在遍历过程中被修改过,这时迭代器会抛出ConcurrentModificationException异常。

代码分析

  • 在这段代码中,首先使用了流操作过滤并去重candidateUsers集合,然后遍历这个集合。
  • 在遍历过程中,如果满足某个条件(即candidateUser包含${),会尝试从expressionService获取表达式的值,并将其添加回candidateUsers集合中。
  • 这种做法违反了遍历过程中的并发修改规则,因此抛出了ConcurrentModificationException异常。

解决方案

为了避免这个问题,可以采用以下几种方法之一。

使用迭代器进行遍历

使用迭代器遍历集合,并在外部循环中处理元素,避免直接修改正在遍历的集合。

这种方法避免了直接修改正在遍历的集合,而是使用迭代器来安全地访问集合中的元素。

if (ObjectUtil.isNotEmpty(candidateUsers)) {candidateUsers = candidateUsers.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());Iterator<String> iterator = candidateUsers.iterator();while (iterator.hasNext()) {String candidateUser = iterator.next();if (ObjectUtil.isNotEmpty(candidateUser) && candidateUser.contains("${")) {try {String expressionValue = expressionService.getStrValue(extendHisprocinst.getProcessInstanceId(), candidateUser);candidateUsers.add(expressionValue);} catch (Exception e) {log.error("解析人员报错,原因:{}", ExceptionUtil.stacktraceToString(e));}}}
}
 
使用临时集合存储结果

创建一个新的集合来存储处理后的结果,而不是直接修改原始集合。从而避免了ConcurrentModificationException异常。

选择其中一种方法即可解决问题。如果需要保留原始集合的内容,建议使用第二种方法(使用临时集合存储结果)。

if (ObjectUtil.isNotEmpty(candidateUsers)) {List<String> tempCandidateUsers = new ArrayList<>();candidateUsers = candidateUsers.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList());for (String candidateUser : candidateUsers) {if (ObjectUtil.isNotEmpty(candidateUser) && candidateUser.contains("${")) {try {String expressionValue = expressionService.getStrValue(extendHisprocinst.getProcessInstanceId(), candidateUser);tempCandidateUsers.add(expressionValue);} catch (Exception e) {log.error("解析人员报错,原因:{}", ExceptionUtil.stacktraceToString(e));}} else {tempCandidateUsers.add(candidateUser);}}candidateUsers = tempCandidateUsers;
}
​

http://www.lryc.cn/news/424049.html

相关文章:

  • Flask数据库操作(第四阶段)
  • C语言问答进阶--5、基本表达式和基本语句
  • uniapp3.0实现图片上传公用组件上传uni-file-picker,uni.uploadFile
  • Unity游戏开发002
  • MySQL基础练习题38-每位教师所教授的科目种类的数量
  • haproxy 原理+实战
  • OSPF进阶
  • SuccBI+低代码文档中心 — 可视化分析(仪表板)(下)
  • 前端创作纪念日
  • 丰收季遇科技之光:北斗卫星导航引领现代农业新篇章
  • 解决windows7虚拟机安装不了vmtools问题
  • Microsoft VBA Excel VBA函数学习笔记——数据切分熟练度+1
  • uniapp获取swiper中子组件的内容高度
  • 基于计算机爱心小屋公益机构智慧管理(源码+论文+部署讲解等)
  • 详细学习PyQt5的样式表与界面美化
  • 遥控器android设备键值原理
  • 零基础也想学编程?Java零基础入门学习路线 + Java教程已准备好!
  • Avnet ZUBoard 1CG开发板上手—深度学习新选择
  • C/C++复习 day1
  • 再见Figma!!新的设计,代码协作神器!【送源码】
  • 快速拷贝复制工具软件@拷贝工具@多线程拷贝@robocopy
  • JavaScript 逆向爬取实战
  • Vue 项目中导入文件时如何默认找寻该文件夹下的 index.vue 文件
  • Idea2023.3.3 —— SourceTree与gitee关联
  • 一文HDMI (High-Definition Multimedia Interface)
  • 【HBZ分享】高并发下如何设计缓存来提升系统性能?
  • 【AI 绘画】 文生图图生图(基于diffusers)
  • 已解决HarmonyOS模拟器卡顿问题
  • C++ | 深入理解C++中的特殊类设计和单例模式(懒汉模式、饿汉模式)
  • Java设计模式之中介者模式