目录
异常代码片段
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;
}