---
url: https://www.chenlj.top/md/java-base/collections/CopyOnWriteArrayList.md
---
# 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]
    }
}
```
