ArrayList高并发可能遇到的ConcurrentModificationException

一、原代码

// 需求背景:多条件判断,获取所有条件,根据优先级排序,执行校验


// 内存中存放了一个自定义的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中进行初始化,预先将修改的操作都执行完,防止出现线上事故

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值