一、原代码
// 需求背景:多条件判断,获取所有条件,根据优先级排序,执行校验
// 内存中存放了一个自定义的condition对象的列表(ArrayList)
List<Condition> conditionList = LocalCache.getConditionList();
// 根据Condition中的order进行优先级排序
conditionList.sort(Comparator.comparingInt(Condition::order));
// 遍历conditionList,执行业务逻辑
for(Condition condition : conditionList){
// 通过校验
if(condition.check()){
// 执行通过校验的逻辑
condition.hit();
} else {
// 执行未通过校验的逻辑
condition.miss();
}
}
二、异常
三、分析原因
ArrayList非线程安全,为了防止并发情况下对数据的脏读,维护了failfast机制,其内部维护了一个modCount用于统计arrayList对象被修改的次数。可以看到源码中执行遍历和排序的方法前(其实大部分方法都做了这个校验),进行了modCount校验,不一致,就会抛出异常ConcurrentModificationException
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
案情还原:
A请求执行sort后执行foreach,假设此时的modCount为1,A中的expectedModCount就是1,A执行foreach内部操作时,B请求执行sort,修改了modCount为2,A执行foreach后,判断expectedModCount与modCount不相等,抛出异常。
四、结论
由于ArrayList非线程安全,如果存在线程共享或者是内存单例,对其modCount值修改的方法,必须隔离在用户请求外,比如可以使用static代码块或者自定义的init中进行初始化,预先将修改的操作都执行完,防止出现线上事故