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
通过写时复制策略,实现了读多写少场景下的线程安全。其源码实现深刻体现了并发编程的精髓:
- 无锁化读操作:读操作无需加锁,通过
volatile
保证可见性。 - 写时复制:写操作时复制新数组,减少锁竞争。
- 弱一致性迭代器:基于数组快照的迭代器,提升迭代性能。
实际开发中,建议优先使用 CopyOnWriteArrayList
,但在写频繁场景下需谨慎使用(因频繁数组复制可能导致性能下降)。深入理解其源码,有助于设计高性能的并发应用程序。