CopyOnWriteArrayList 源码解析
简介
CopyOnWriteArrayList 位于 java.util.concurrent 包下,是 ArrayList 的线程安全变体,核心思想是 「写时复制(Copy-On-Write)」:
- 读操作无锁:直接读取底层数组(通过
volatile保证可见性),完全不需要加锁。 - 写操作加锁并复制:每次修改都会复制一份新数组,在新数组上修改,再原子地替换引用。
它特别适合读多写极少的场景(如监听器列表、配置快照);写多或元素很大的场景要避免使用(每次写都要复制整个数组,内存与 CPU 开销大)。
注意:迭代器持有的是创建时刻的数组快照,迭代过程中原列表的修改不会影响它,因此永远不会抛出
ConcurrentModificationException,但也可能读不到最新数据(弱一致性)。
底层数据结构分析
内部只维护一个 volatile 的 Object[] 数组,以及一个用于写操作的 ReentrantLock。读者读的是数组当前引用指向的数组;写者加锁后复制出新数组,改完用 setArray 替换引用——由于数组引用是 volatile 的,读线程能立刻看到新数组。
java
// 真正存储元素的数组,所有读都基于它的快照
private transient volatile Object[] array;
// 保护写操作的独占锁
final transient ReentrantLock lock = new ReentrantLock();
final Object[] getArray() { return array; }
final void setArray(Object[] a) { array = a; }核心成员变量
java
private transient volatile Object[] array; // 底层数组(volatile 保证可见性)
final transient ReentrantLock lock = new ReentrantLock(); // 写时复制的互斥锁源码分析
写入:add(复制整个数组)
java
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock(); // 1. 加锁,保证写之间互斥
try {
Object[] elements = getArray();
int len = elements.length;
// 2. 复制出一份 len+1 的新数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e; // 3. 在新数组末尾追加
setArray(newElements); // 4. 原子替换引用(volatile 写,读线程立即可见)
return true;
} finally {
lock.unlock();
}
}读取:get(完全无锁)
java
public E get(int index) {
return (E) getArray()[index]; // 直接读 volatile 数组,无需加锁
}删除:remove(同样复制)
java
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = (E) elements[index];
int numMoved = len - index - 1;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1)); // 删最后一个
else {
Object[] newElements = new Object[len - 1]; // 复制并跳过被删元素
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index, numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}要点:写操作的时间与空间复杂度都是 O(n)(要复制整个数组);而读是 O(1) 且无锁。
与 ArrayList / Vector 的对比
| 维度 | ArrayList | Vector | CopyOnWriteArrayList |
|---|---|---|---|
| 线程安全 | 否 | 是(方法级 synchronized) | 是(写锁 + 复制) |
| 读性能 | O(1) | O(1) 但加锁 | O(1) 且无锁 |
| 写性能 | O(1) 均摊 | 慢(全局锁) | 慢(复制整个数组) |
| 迭代一致性 | fail-fast | fail-fast | 快照(弱一致) |
经典 Demo
java
import java.util.concurrent.CopyOnWriteArrayList;
public class COWDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("a"); list.add("b");
// 迭代器是快照,迭代时修改不会影响它
for (String s : list) {
System.out.println(s);
list.add("x"); // 不会影响正在进行的迭代
}
System.out.println(list); // [a, b, x, x]
}
}