system.arraycopy()是唯一具备操作原子性的数组拷贝方法,其原子性源于jvm底层内存搬运、一次性参数校验和无java层干预;但原子性不等于线程安全,多线程下仍需配合不可变源、引用替换或原子数组等机制保障并发正确性。

数组拷贝本身不天然具备跨线程的原子性保障,真正能提供“执行不可中断”语义的,只有 System.arraycopy() 的单次调用——它在 JVM 层完成一次底层内存块搬运,要么全成功,要么抛异常,中间状态对外不可见。但这只是操作原子性,不是线程安全性。
System.arraycopy() 的原子性本质
它是 Java 中唯一绕过 Java 字节码、直接触发 JVM 底层内存搬运(如 memmove 或 SIMD 指令)的原语。其原子性体现在:
- 参数校验一次性完成:null、越界、负长度等错误都在拷贝开始前抛出,不会出现“部分拷贝”
- 重叠拷贝方向自动适配:src 和 dest 为同一数组时,JVM 根据 srcPos 与 destPos 关系决定正序或倒序搬运,结果语义等同 C 的 memmove
- 执行过程无 Java 层干预:不经过循环、不触发边界检查、不调用对象方法,避免 JIT 干扰和 GC write barrier
Arrays.copyOf() 和 clone() 不额外提供原子性
这两个方法内部都委托给 System.arraycopy(),但自身包含额外步骤:
- Arrays.copyOf():先 new 数组(可能触发 GC)、再 arraycopy、最后填充默认值——整个流程非原子,其中任意一步都可能被中断或并发干扰
- clone():对一维数组做了 JVM 特殊优化,行为近似 arraycopy,但仅限浅拷贝且无法指定偏移;若数组元素是可变对象,clone 后仍共享引用,不解决逻辑层面的竞态
多线程场景下,原子性 ≠ 线程安全
即使单次 arraycopy 是原子的,以下情况仍会导致数据错乱:
- 多个线程往同一个 dest 数组不同区域写入,CPU 缓存未及时同步,造成读到“撕裂”数据
- 源数组在拷贝过程中被其他线程修改,导致读到中间状态(例如扩容中的 ArrayList 内部数组)
- 拷贝后通过 volatile 引用替换目标数组,但替换前未确保所有线程已停止读取旧数组
真正安全的实践路径
要兼顾性能与并发正确性,关键不在“怎么拷”,而在“谁在读、谁在写、何时切换”:
- 优先采用不可变源 + 独立目标 + 引用替换:新数组构建完毕后,用 AtomicReference.lazySet 替换旧引用,读线程始终看到完整快照
- 避免共享 dest:每个线程使用自己的 buffer,合并阶段再用一次 arraycopy 写入最终结果
- 对高频更新的数值型数组,改用 AtomicIntegerArray 等原子数组类,按索引粒度保证 CAS 更新的原子性和可见性
- 若必须拷贝含可变对象的数组,浅拷贝后需手动遍历深拷贝每个元素,否则引用共享会绕过所有拷贝机制的保护











