You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ArrayList.removeAt()触发偶发IndexOutOfBoundsException问题求助

偶发ArrayIndexOutOfBoundsException排查与修复

问题根源

ArrayList本身是非线程安全的容器,哪怕你认为同一时间只有一个Worker实例运行,仍可能出现以下触发异常的场景:

  • 并发操作冲突:Worker线程在处理任务时,其他线程(比如主线程、回调线程)可能同时对mTaskList执行添加、删除操作。比如Worker刚判断完列表不为空,还没执行removeAt(0),另一个线程就把唯一的元素删掉了,此时执行remove就会抛出Index: 0, Size: 0的异常。
  • 索引计算错误:出现index=-1的异常,要么是代码某处错误地传入了-1作为索引调用removeAt,要么是并发场景下列表size被其他线程修改,导致索引计算逻辑出错(比如原本想取size-1,但size被改成0,就得到了-1)。

绝对不能忽略这个异常

这种低概率崩溃本质是线程安全漏洞,会引发一系列问题:

  • 任务丢失:待处理的任务可能被意外丢弃或无法正常执行
  • 数据混乱:列表状态出现不一致,导致后续任务处理逻辑异常
  • 崩溃量累积:单次执行概率低,但用户基数大时,崩溃总数会显著上升

修复方案

  • 使用线程安全的列表包装:把new ArrayList<>()替换为Collections.synchronizedList(new ArrayList<>()),确保所有对列表的操作(add、remove、size查询等)都经过同步处理。
  • 添加显式锁控制:在所有访问mTaskList的代码块(包括添加任务、Worker处理任务的逻辑)外层,加同一个ReentrantLock,保证同一时间只有一个线程能操作列表。
  • 替换为线程安全队列:既然是任务处理场景,推荐用LinkedBlockingQueue替代ArrayList,它天然支持线程安全的生产者-消费者模式,无需手动处理同步逻辑。
  • 增加防御性检查(兜底):在执行removeAt(0)前先判断列表是否为空,代码示例:
if (!mTaskList.isEmpty()) {
    mTaskList.removeAt(0);
}

注意:这只是临时兜底方案,不能解决根本的线程安全问题,必须配合前面的同步措施使用。

内容的提问来源于stack exchange,提问作者Youja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 02:22:18