copyonwritearraylist 的写时复制机制核心是读写分离、数组快照与原子引用更新:读操作直接访问 volatile 数组,无锁高效;写操作需加锁、复制数组、修改副本、volatile 原子替换引用,确保线程安全但带来 o(n) 时间开销和内存压力。

CopyOnWriteArrayList 的写时复制底层机制,核心是“读写分离 + 数组快照 + 原子引用更新”。它不靠锁保护读,也不在原数组上改数据,而是用空间换线程安全。
读操作:直接访问 volatile 数组,完全无锁
每次 get、contains、size 等读方法,都直接从当前 volatile Object[] array 中取值。因为 array 是 volatile 修饰的,写操作完成后,所有线程能立即看到新数组的引用,但读过程本身不加锁、不阻塞、不复制。
- 迭代器(iterator)也是读的一种——它在创建时就调用 getArray() 拿到当前数组副本,后续遍历全程基于这个快照,所以不会抛 ConcurrentModificationException
- 这意味着:迭代过程中其他线程 add/remove 了元素,迭代器也看不到,它看到的是“那一刻”的静态视图
写操作:加锁 → 复制 → 修改 → 替换引用
所有写方法(add、remove、set、addAll 等)都走同一套流程:
- 先获取 ReentrantLock lock,确保同一时刻只有一个写线程执行
- 调用 getArray() 获取当前底层数组(旧快照)
- 用 Arrays.copyOf() 创建一个新数组(长度按需调整),把旧数据拷过去
- 在新数组上完成修改(如末尾追加、指定索引赋值、过滤删除等)
- 调用 setArray(newArray) 原子性更新 volatile 引用,让后续读操作自然切换到新数组
为什么需要 volatile + 锁配合?
volatile 保证数组引用的可见性,但不保证复合操作的原子性。比如“读旧数组 → 复制 → 写新数组 → 更新引用”这整段必须串行执行,否则多个写线程可能基于同一个旧数组各自复制,最后只保留其中一个结果,造成数据丢失。所以锁负责排他性,volatile 负责新数组对所有读线程的及时可见。
内存与一致性代价很明确
每次写都要复制整个数组,意味着:
- 写操作时间复杂度是 O(n),且随元素数量线性增长
- 短时间内高频写会触发大量对象创建,加重 GC 压力
- 读到的数据不是实时的——写入后,正在迭代的线程仍看到旧快照;刚写完立刻 get 可以看到,但迭代器或稍早获取的快照看不到
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











