并发多线程下的 ArrayList 数据丢失案例:通过源码分析由于 size++ 非原子操作产生的索引覆盖问题

千浩姑娘_7919

千浩姑娘_7919

2026-05-01

962人浏览

原创

arraylist在多线程下非线程安全,核心在于add()中size++的非原子性(读-改-写三步分离),导致竞态覆盖与size少计;扩容时多线程各自复制数组并覆盖引用,加剧数据丢失;常见静默丢数而非抛arrayindexoutofboundsexception。

并发多线程下的 arraylist 数据丢失案例:通过源码分析由于 size++ 非原子操作产生的索引覆盖问题

ArrayList 在多线程环境下不是线程安全的,核心问题出在 add() 方法中 size++ 操作的非原子性,导致多个线程同时扩容、写入时发生索引覆盖,最终出现数据丢失或数组越界等异常。

add() 方法的非原子三步操作

调用 add(E e) 时,实际执行三个分离步骤:

  • 检查容量是否足够(ensureCapacityInternal)——可能触发扩容
  • 将元素赋值到 elementData[size] 位置
  • 执行 size++(即 size = size + 1)

其中第2、3步看似连贯,但 size++ 是“读取-修改-写入”三阶段操作,在 JVM 层并非原子指令。当两个线程 T1、T2 同时执行到这一步,可能出现如下竞态:

  • T1 读取 size = 9
  • T2 也读取 size = 9
  • T1 计算 9+1=10,写回 size = 10,并把元素存入 elementData[9]
  • T2 同样计算 9+1=10,写回 size = 10,并把元素也存入 elementData[9]

结果:两个元素都写到了下标 9,前一个被覆盖;size 最终为 10(应为 11),逻辑长度少计 1。

扩容过程加剧竞争风险

当多个线程触发扩容(如初始容量为 0 或满载),会进入 grow() 方法重新分配数组并复制旧数据。此时若未同步:

魔搭GPT(ModelScopeGPT)
魔搭GPT(ModelScopeGPT)

魔搭GPT(ModelScopeGPT)是一款AI视频创作工具,阿里达摩院推出的大小模型协同的智能助手,具备作诗、绘画、视频生成、语音播放等多模态能力。

下载
  • 两个线程可能各自创建新数组,各自复制一次旧数据
  • 后完成的线程用新数组覆盖了 elementData 引用,导致先完成线程的复制结果丢失
  • 更严重的是:复制过程中 size 可能已被其他线程修改,造成复制数量不准(如复制了 9 个,但实际已有 10 个)

这种“复制-赋值”分离,叠加 size 状态不同步,使数据丢失从偶发变为高概率事件。

为什么不会抛 ArrayIndexOutOfBoundsException?

很多人误以为并发 add 会直接越界报错,其实不然:

  • 扩容判断基于旧 size 值,只要任一线程提前扩容成功,elementData 就已变长
  • 后续线程即使读到过期 size,写入的下标仍大概率在新数组范围内(如 size=9 写 elementData[9],而新数组长度已是 16)
  • 真正报错往往出现在极端情况:多个线程在无锁下反复扩容失败、size 错乱后,某次写入超出所有已知容量

所以生产环境更常见的是“静默丢失”——日志没报错,但业务数据莫名少了一条。

验证与替代方案

可通过循环启动 100 个线程,每个向同一 ArrayList 添加 1000 个不同元素,最终 size 很可能

安全替代方式包括:

  • Vector:add 方法加了 synchronized,但性能差、已不推荐
  • Collections.synchronizedList(new ArrayList()):手动同步,但仅方法级,遍历仍需额外同步块
  • CopyOnWriteArrayList:适合读多写少,写操作复制整个数组,无锁但内存和 CPU 开销大
  • 使用阻塞队列(如 LinkedBlockingQueue)或并发集合(ConcurrentHashMap 模拟列表语义)

相关专题

更多
Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

2025.12.24

1049

20

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

2026.01.21

363

30

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

2026.01.21

519

24

C# 多线程与异步编程
C# 多线程与异步编程

本专题深入讲解 C# 中多线程与异步编程的核心概念与实战技巧,包括线程池管理、Task 类的使用、async/await 异步编程模式、并发控制与线程同步、死锁与竞态条件的解决方案。通过实际项目,帮助开发者掌握 如何在 C# 中构建高并发、低延迟的异步系统,提升应用性能和响应速度。

2026.02.06

369

20

C++多线程并发控制与线程安全设计实践
C++多线程并发控制与线程安全设计实践

本专题围绕 C++ 在高性能系统开发中的并发控制技术展开,系统讲解多线程编程模型与线程安全设计方法。内容包括互斥锁、读写锁、条件变量、原子操作以及线程池实现机制,同时结合实际案例分析并发竞争、死锁避免与性能优化策略。通过实践讲解,帮助开发者掌握构建稳定高效并发系统的关键技术。

2026.03.16

213

22

Java 多线程与并发编程实战
Java 多线程与并发编程实战

深入讲解 Java 并发编程体系,涵盖 Thread / Runnable / Callable 线程创建方式、线程生命周期与状态转换、synchronized 同步锁与 Lock/ReentrantLock 显式锁、volatile 可见性保证、ThreadPool 线程池配置与调优(核心参数/拒绝策略)、JUC 并发工具类(CountDownLatch/CyclicBarrier/Semaphore/ConcurrentHashMa

2026.04.17

309

31

C++ 多线程使用教程大全
C++ 多线程使用教程大全

系统讲解 C++11 及后续标准引入的多线程编程能力,涵盖 std::thread 线程创建与生命周期管理、std::mutex / shared_mutex 互斥锁与读写锁、std::condition_variable 条件变量同步、std::atomic 原子操作与内存序(memory_order)、std::future / promise / async 异步任务、线程池的设计与实现、死锁检测与避免策略,帮助开发者编写正确高效

2026.04.28

196

17

Python多线程、多进程与并发编程教程大全
Python多线程、多进程与并发编程教程大全

系统讲解 Python 的并发与并行编程体系,涵盖 GIL 全局解释器锁的原理与影响分析、threading 模块的线程创建/锁/事件/信号量、multiprocessing 模块的进程创建/进程间通信(Queue/Pipe/共享内存)、concurrent.futures 线程池与进程池的统一接口、I/O 密集型与 CPU 密集型任务的方案选择、多线程竞态条件排查与线程安全数据结构、subprocess 子进程管理,帮助开发者根据任务

2026.05.08

174

32

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习