CopyOnWriteArrayList的实现原理详解

最近正在复习Java八股,所以会将一些热门的八股问题,结合ai与自身理解写成博客便于记忆。

一、CopyOnWriteArrayList概述

CopyOnWriteArrayList是Java并发包(`java.util.concurrent`)中的一个线程安全的List实现,它采用了"写时复制"(Copy-On-Write)策略来保证线程安全。

基本特性:
- 线程安全,无需额外同步
- 适用于读多写少的场景
- 迭代器不会抛出ConcurrentModificationException
- 每次修改操作都会创建底层数组的新副本

二、核心实现原理

1. 数据结构

CopyOnWriteArrayList内部使用一个volatile数组来存储元素:

private transient volatile Object[] array;

volatile关键字保证了数组引用的可见性,任何线程都能看到最新的数组引用。

2. 读操作实现

读操作(如get、size等)直接访问当前数组,无需任何同步控制:

public E get(int index) {
    return get(getArray(), index);
}

private E get(Object[] a, int index) {
    return (E) a[index];
}

由于数组本身是不可变的(每次修改都会创建新数组),所以读操作是线程安全的。

3. 写操作实现

写操作(如add、set、remove等)会加锁并创建新数组:

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();
    }
}

写操作的典型流程:
1. 获取独占锁
2. 复制原数组到新数组
3. 在新数组上执行修改
4. 将新数组设置为当前数组
5. 释放锁

4. 迭代器实现

CopyOnWriteArrayList的迭代器是对创建迭代器时的数组快照进行操作:

public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}

由于迭代器操作的是不变的数组快照,所以不会抛出ConcurrentModificationException,但也无法反映创建迭代器后的修改。

三、关键设计思想

1. 读写分离

- 读操作:无锁,直接访问当前数组
- 写操作:加锁,操作数组副本

2. 最终一致性

写操作对数组的修改对其他线程不是立即可见的,直到新数组被设置。这提供了最终一致性而非强一致性。

3. 空间换时间

通过牺牲写操作的性能(复制数组)来换取读操作的高性能。

四、适用场景

CopyOnWriteArrayList最适合以下场景:

1. 读多写少:如事件监听器列表、配置信息等
2. 集合大小通常保持较小:因为复制大数组开销大
3. 需要避免迭代时的并发修改异常

五、性能分析

优点:
- 读操作性能极高,接近普通ArrayList
- 线程安全且无需额外同步
- 迭代安全,不会抛出ConcurrentModificationException

缺点:
- 写操作性能差,每次修改都要复制整个数组
- 内存占用高,特别是集合较大时
- 数据一致性是最终一致性,不能保证强一致性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值