CopyOnWriteArrayList通过读写分离实现线程安全:读操作无锁访问volatile数组快照,写操作复制数组并原子更新引用,读写互不阻塞,适用于读多写少场景。

CopyOnWriteArrayList 实现读写分离的核心思路是:读操作完全无锁,写操作通过“复制+替换”实现线程安全,天然隔离读写路径。它不依赖 synchronized 或 CAS 等同步机制来协调读写,而是用空间换时间——每次写都新建数组副本,写完再原子更新引用,读则始终访问当前快照,因此读写互不阻塞。
读操作:直接读取当前数组引用,零同步开销
所有读方法(如 get()、size()、iterator())都直接访问内部的 volatile 数组引用(array),不做任何加锁或拷贝:
- get(i) 直接返回
array[i],只要下标合法,不检查是否越界(由调用方保证) - iterator() 返回的迭代器持有创建时的数组快照,后续即使列表被修改,迭代仍基于原数组进行
- 因为
array是 volatile 的,读线程总能见到最新已发布的数组引用,无需额外同步
写操作:复制原数组 → 修改副本 → 原子替换引用
add()、remove()、set() 等写方法严格遵循三步流程:
- 获取当前数组的快照(引用)
- 创建新数组(长度 ±1 或相同),将原内容复制过去,并在副本上完成修改
- 用 volatile 写语义更新
array字段,使新数组对所有读线程可见
例如 add(E e):先 Arrays.copyOf(array, len + 1),再设最后一个元素为 e,最后 setArray(newArray)。整个过程没有锁,但写操作之间是串行化的(因 array 引用更新是原子的,且多线程写会竞争成功一次)。
立即学习“Java免费学习笔记(深入)”;
读写真正“分离”的关键点
这种分离不是靠锁分段或读写锁,而是靠数据不可变性(每次读看到的是某个稳定快照)和引用的 volatile 可见性保障:
- 读不阻塞写,写也不阻塞读;多个读线程可并发执行,无竞争
- 写操作期间,读线程仍能安全访问旧数组,不会出现脏读或数组正在被修改的中间态
- 迭代器弱一致性:遍历时看不到写操作的实时结果,但也不会抛
ConcurrentModificationException
适用场景与注意事项
适合读远多于写的场景(如监听器列表、配置项缓存),但要注意:
- 写操作内存开销大,频繁增删会导致大量临时数组和 GC 压力
- 写操作不是实时对所有读可见——新写入的元素,正在遍历的迭代器可能永远看不到
- 不支持在迭代过程中调用
list.remove()等结构修改方法(会抛UnsupportedOperationException)


















