CopyOnWriteArrayList详解


1️⃣ 什么是 CopyOnWriteArrayList

  • java.util.concurrent 包下的 线程安全的 List
  • 读多写少场景下的性能优选
  • 核心思想:写时复制(Copy-On-Write)

2️⃣ 底层原理

  • 内部维护一个 volatile Object[] array
  • 读操作:直接读取数组,不加锁,性能极高
  • 写操作(增删改)
    • ReentrantLock 互斥锁
    • 把原数组 复制一份新数组
    • 在新数组上操作
    • 操作完成后,替换原数组引用(volatile 保证可见性)

核心源码(add()

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

可以看到: ✅ 每次写都是复制一份新数组
✅ 写的时候加锁
✅ 读的时候完全无锁直接读取数组


3️⃣ 适用场景(非常重要⚠️)

读多写少 比如:

  • 监听器列表
  • 配置缓存
  • 黑白名单
  • 菜单、字典缓存

读操作 非常频繁,写操作 偶尔才发生


4️⃣ 优缺点总结

优点 ✅ 缺点 ❌
读操作无锁,性能极高 写操作开销大,涉及数组复制(O(n))
读写分离,读操作不会阻塞 不适合高并发写场景(大量写时性能很差)
线程安全,避免并发修改异常(fail-safe 内存占用大,频繁写会导致大量冗余对象产生

5️⃣ 面试/实战高频知识点

✅ 为什么适合读多写少?

读不加锁,直接基于 volatile array 读取,超快!

✅ 为什么不会 ConcurrentModificationException

因为写操作直接复制新数组,读操作永远看旧数组,天然 fail-safe

✅ 和 ArrayList 区别?

特性 ArrayList CopyOnWriteArrayList
线程安全 ❌ 否 ✅ 是
读性能 快(无锁)
写性能 慢(需要复制)
适用场景 单线程 or 手动加锁 读多写少并发场景

6️⃣ 注意⚠️

  • Iterator 遍历时,基于快照,线程安全,但不会感知后续的写入
for (String s : cowList) {
    // 安全,遍历的是旧数组
}
  • 不适合频繁写的场景!

7️⃣ 示例代码

import java.util.concurrent.CopyOnWriteArrayList;

public class Demo {
    public static void main(String[] args) {
        CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        // 读操作无锁,超快
        list.forEach(System.out::println);

        // 写时复制,线程安全
        list.add("D");
    }
}

✅ 总结(背下来面试直接用)

CopyOnWriteArrayList 是线程安全的 List,核心是写时复制(Copy-On-Write)。
读操作无锁,性能高;写操作加锁并复制新数组,适合读多写少场景。
天然避免 ConcurrentModificationException,遍历安全。

你可能感兴趣的:(java,开发语言,集合)