Skip to content

CopyOnWriteArrayList:读多写少场景的列表

CopyOnWriteArrayList 是什么

写时复制策略:每次修改都复制全量数组,读取无需加锁。

java
// 读取时完全无锁
List<String> list = new CopyOnWriteArrayList<>();
list.get(0); // 无锁读取
list.size(); // 无锁读取

写入时复制整个数组:

java
public boolean add(E e) {
    synchronized (lock) {
        Object[] newElements = Arrays.copyOf(elements, elements.length + 1);
        newElements[elements.length] = e;
        elements = newElements;
    }
}

vs ArrayList

特性ArrayListCopyOnWriteArrayList
线程安全
读性能极快(无锁)
写性能慢(全量复制)
迭代安全Fail-FastFail-Safe
内存开销高(每次写都复制)

典型应用场景

读多写少的配置列表

java
CopyOnWriteArrayList<Rule> rules = new CopyOnWriteArrayList<>();

// 高频读取规则
public List<Rule> getRules() {
    return rules; // 返回快照,多线程读取无锁
}

// 偶尔修改规则
public void addRule(Rule rule) {
    rules.add(rule);
}

监听器列表

java
CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();

// 注册监听器(写操作)
public void addListener(Listener l) {
    listeners.add(l);
}

// 触发监听器(读操作)
public void fireEvent(Event e) {
    for (Listener l : listeners) {
        l.onEvent(e); // 遍历时无锁
    }
}

常见问题

问题 1:写操作性能差

java
// ❌ 大数据量写操作性能极差
CopyOnWriteArrayList<List<String>> nested = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
    nested.add(generateLargeList()); // 每次都复制全量数组
}
// 10000 次添加,复制了 1+2+3+...+10000 次 = 5000 万次元素拷贝

问题 2:内存占用高

每次写操作都会产生新的数组副本,旧数组被 GC 回收前占用双份内存。


总结

要点说明
读写特性读极快(无锁),写极慢(全量复制)
适用场景读多写少(监听器列表、配置等)
迭代安全Fail-Safe,遍历时无需同步
局限性不适合写多场景,内存开销大

一句话:CopyOnWriteArrayList 是「读多写少」场景的列表,用全量复制换无锁读取。


相关链接

基于 VitePress 构建