Java并发程序中ArrayList元素丢失问题的原理咨询
多线程下ArrayList元素丢失的底层原理分析
一、ArrayList的核心实现逻辑
ArrayList基于动态数组实现,内部有两个关键字段:
elementData:存储元素的底层数组size:当前列表中已有的元素数量
它的add()方法核心逻辑可简化为:
public boolean add(E e) { // 确保数组容量足够容纳新元素 ensureCapacityInternal(size + 1); // 先把元素放到数组的size位置,再将size自增1 elementData[size++] = e; return true; }
二、多线程竞态条件导致元素丢失的具体过程
当两个线程同时对同一个ArrayList执行add()操作时,会触发竞态条件,直接造成元素丢失,具体场景如下:
假设当前size为10,线程A和线程B同时执行add():
- 线程A执行
ensureCapacityInternal(11),确认数组容量足够,准备执行elementData[10] = 'K',但还没执行size++就被CPU挂起。 - 线程B也执行
ensureCapacityInternal(11),同样确认容量足够,直接执行elementData[10] = 'k',覆盖了线程A即将写入的位置,随后执行size++,size变为11。 - 线程A恢复执行,跳过容量检查(之前已完成),直接执行
size++,size变为12。
最终结果:数组第10位仅保存了线程B的元素,线程A的'K'被覆盖丢失;但size却增加了2,实际只新增1个元素,所以最终列表总大小会比预期的52少1(比如你遇到的51)。
三、为什么调试时letter变量存在但元素丢失
你调试时看到addLetter的局部变量letter包含丢失的'K',是因为线程确实正确传递了该字符到addLetter方法中,但在ArrayList的add()操作里,赋值步骤被另一个线程覆盖,导致这个字符没有成功保存到底层数组,最终列表中看不到它。
四、解决方法
- 改用线程安全的集合类:比如
CopyOnWriteArrayList(适合读多写少场景),或者用Collections.synchronizedList(new ArrayList<>())包装ArrayList。 - 给
Data类的addLetter方法添加synchronized修饰,或者在调用addLetter时加锁,确保同一时间只有一个线程执行元素添加操作。
内容的提问来源于stack exchange,提问作者CDO
相关产品推荐
相关产品推荐

