JDK 8 CopyOnWriteArrayList 详解及详细源码展示

JDK 8 CopyOnWriteArrayList 详解及详细源码展示

JDK 8 的 CopyOnWriteArrayList 是 Java 并发包中的核心实现,基于 写时复制(Copy-On-Write) 策略,适用于读多写少的并发场景。其核心思想是:写操作时复制新数组,读操作直接访问原数组,通过空间换时间的方式实现线程安全。本文结合源码剖析其设计哲学、核心实现及性能优化技术,并添加关键代码步骤注释。

一、核心设计目标:读多写少场景的线程安全
1.1 写时复制策略
  • 写操作加锁:通过 ReentrantLock 保证写操作的原子性。
  • 复制新数组:写操作时复制原数组,修改新数组后替换原数组。
  • 读操作无锁:读操作直接访问原数组,无需加锁。
1.2 适用场景
  • 读多写少:如事件监听器列表、缓存配置等。
  • 迭代安全:迭代过程中允许修改,不会抛出 ConcurrentModificationException
二、源码核心类结构
// java.util.concurrent.CopyOnWriteArrayList.java
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
    // 内部锁
    private final transient ReentrantLock lock = new ReentrantLock();
    // 底层数组(volatile 保证可见性)
    private transient volatile Object[] array;

    // 构造方法
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    public CopyOnWriteArrayList(Collection<? extends E> c) {
        setArray(c.toArray());
    }
}
三、核心方法源码解析
3.1 add 方法
public boolean add(E e) {
    // 1. 获取锁(保证写操作的原子性)
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 2. 获取原数组
        Object[] elements = getArray();
        int len = elements.length;
        // 3. 复制新数组(容量 +1)
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        // 4. 添加新元素
        newElements[len] = e;
        // 5. 替换原数组
        setArray(newElements);
        return true;
    } finally {
        // 6. 释放锁
        lock.unlock();
    }
}
3.2 get 方法
public E get(int index) {
    // 1. 直接访问原数组(无锁)
    return elementAt(getArray(), index);
}

final Object[] getArray() {
    return array;
}

E elementAt(Object[] a, int index) {
    // 2. 返回指定索引的元素
    return (E) a[index];
}
3.3 set 方法
public E set(int index, E element) {
    // 1. 获取锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 2. 获取原数组
        Object[] elements = getArray();
        E oldValue = elementAt(elements, index);
        // 3. 复制新数组
        if (oldValue != element) {
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len);
            newElements[index] = element;
            // 4. 替换原数组
            setArray(newElements);
        }
        return oldValue;
    } finally {
        // 5. 释放锁
        lock.unlock();
    }
}
3.4 remove 方法
public E remove(int index) {
    // 1. 获取锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 2. 获取原数组
        Object[] elements = getArray();
        int len = elements.length;
        E oldValue = elementAt(elements, index);
        int numMoved = len - index - 1;
        // 3. 复制新数组(跳过被删除元素)
        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 {
        // 4. 释放锁
        lock.unlock();
    }
}
3.5 iterator 方法
public Iterator<E> iterator() {
    // 1. 返回弱一致性迭代器(基于原数组快照)
    return new COWIterator<E>(getArray(), 0);
}

static final class COWIterator<E> implements ListIterator<E> {
    // 2. 迭代器基于数组快照,后续修改不影响迭代
    private final Object[] snapshot;
    private int cursor;

    private COWIterator(Object[] elements, int initialCursor) {
        cursor = initialCursor;
        snapshot = elements;
    }

    public boolean hasNext() {
        return cursor < snapshot.length;
    }

    public E next() {
        if (!hasNext())
            throw new NoSuchElementException();
        return (E) snapshot[cursor++];
    }
}
四、关键优化技术
4.1 写时复制优化
  • 锁粒度控制:仅锁住写操作,读操作无锁。
  • 数组复制策略:通过 Arrays.copyOf 优化数组复制性能。
4.2 弱一致性迭代器
  • 快照机制:迭代器基于数组快照,后续修改不影响迭代。
  • 无锁迭代:迭代过程中无需加锁,提升性能。
4.3 内存局部性优化
  • 连续内存:元素在内存中连续存储,提升缓存命中率。
  • 数组复制:通过 System.arraycopy 优化数组复制性能。
五、典型应用场景
5.1 基础操作
// 创建 CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();

// 添加元素
list.add("A");
list.add("B");

// 读取元素
String element = list.get(1); // "B"

// 删除元素
list.remove(0);
5.2 并发安全迭代
// 迭代过程中允许修改
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    if (item.equals("A")) {
        list.add("C"); // 不会抛出 ConcurrentModificationException
    }
}
5.3 事件监听器列表
// 线程安全的事件监听器列表
CopyOnWriteArrayList<EventListener> listeners = new CopyOnWriteArrayList<>();

// 添加监听器
listeners.add(new EventListener() {
    public void onEvent(Event e) {
        // 处理事件
    }
});

// 触发事件(迭代安全)
for (EventListener listener : listeners) {
    listener.onEvent(event);
}
六、总结

JDK 8 的 CopyOnWriteArrayList 通过写时复制策略,实现了读多写少场景下的线程安全。其源码实现深刻体现了并发编程的精髓:

  1. 无锁化读操作:读操作无需加锁,通过 volatile 保证可见性。
  2. 写时复制:写操作时复制新数组,减少锁竞争。
  3. 弱一致性迭代器:基于数组快照的迭代器,提升迭代性能。

实际开发中,建议优先使用 CopyOnWriteArrayList,但在写频繁场景下需谨慎使用(因频繁数组复制可能导致性能下降)。深入理解其源码,有助于设计高性能的并发应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值