System.arraycopy本身不能防范数据断层污染,它不是同步机制;断层源于多线程对共享数组的未受控读写交织,防范关键在于明确数组角色(src只读、dest独占)、用同步块包裹“读–复制–写”全过程,或采用不可变+引用替换模式。

System.arraycopy 本身不能防范数据断层污染,它不是同步机制,也不提供并发保护。所谓“断层”,比如部分字段是旧值、部分已是新值,或某次拼装漏掉几行数据,根源在于多个线程对共享数组的未受控读写交织,而非 arraycopy 调用本身出了问题。
关键不在“怎么用 arraycopy 防”,而在于“怎么组织 arraycopy 的上下文,让它不暴露在竞态中”。
明确数组角色:谁读、谁写、谁复制
断层最常发生在“一边改、一边抄”的场景。必须厘清每个数组的职责:
-
源数组(src)应尽量只读:初始化后不再修改,例如配置快照、报表模板、预加载维度数据。此时
arraycopy搬出的永远是一致快照。 -
目标数组(dest)应由当前线程独占:不要复用全局缓冲区;优先在栈上
new int[1024]或new String[n],复制完即用即弃。 - 若必须复用共享 dest(如环形日志缓冲),则需确保:
- 多个线程写入区域完全不重叠(如线程 A 写 [0,99],线程 B 写 [100,199])
- 且没有任何线程在写的同时,另一个线程正在遍历或导出该区域
同步仅包裹“读–复制–写”全过程,而非单个 arraycopy
仅给 arraycopy 加锁没用——断层可能来自锁外的写操作。真正要保护的是整个临界区:
- 使用统一锁对象(推荐私有
final Object lock = new Object();),避免锁src或dest数组实例(易被外部误锁) - 同步块内应包含:读取源状态 → 调用
arraycopy→ 更新目标引用或标记就绪 - 示例:
synchronized (lock) { // 确保 src 此刻稳定,无其他线程正在修改 System.arraycopy(srcData, 0, destBuffer, 0, srcData.length); // 紧接着发布结果,如设置 volatile 标志或替换引用 ready = true; }
用不可变+引用替换代替原地修改
比硬加锁更轻量、更可靠的模式是放弃“共享可变数组”:
- 每次更新都生成新数组(如
int[][] newData = new int[N][]),再逐行用arraycopy填充子数组 - 拼装完成,用
volatile字段原子替换引用:private volatile int[][] currentReport; // …拼装完毕后 currentReport = newData; // 对读线程立即可见
- 读线程直接使用
currentReport,全程无锁;写线程互不影响
避开典型误用陷阱
这些做法看似用了 arraycopy,实则无法防断层:
- 在
ArrayList扩容过程中让其他线程并发读其elementData:扩容时旧数组还在被读,新数组尚未就绪,arraycopy搬的是中间态 - 把
volatile int[][] data当成线程安全:volatile只保证data引用更新可见,不保证data[0][1]的读写同步 - 多线程向同一
dest不同索引调用arraycopy,但未校验区间是否真不重叠(例如计算偏移时整数溢出或逻辑错位)
不复杂但容易忽略。

















