在多线程环境中,传统的ArrayList面临严重的线程安全问题。Java提供了多种并发容器解决方案,其中CopyOnWriteArrayList
(简称COW List)采用了一种独特而巧妙的并发策略——写时复制(Copy-On-Write)。
本文将深入剖析CopyOnWriteArrayList
的底层原理(基于JDK 17源码),并与ThreadLocal
的线程隔离策略进行对比,帮助读者理解不同并发场景下的最佳选择。
CopyOnWriteArrayList的核心思想是:
这种机制保证:
特性 | ThreadLocal | CopyOnWriteArrayList |
---|---|---|
并发策略 | 线程隔离(空间换时间) | 写时复制(时间换空间) |
数据可见性 | 线程私有 | 全局共享 |
写操作开销 | 低(直接修改线程本地副本) | 高(需要复制整个数组) |
读操作开销 | 低 | 极低(无锁) |
适用场景 | 线程上下文数据 | 读多写少的共享集合 |
javapublic class CopyOnWriteArrayList<E> {
// 使用volatile保证数组引用的可见性
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}
}
javapublic boolean add(E e) {
synchronized (lock) { // 全局锁保证写操作互斥
Object[] elements = getArray();
int len = elements.length;
// 复制新数组(长度+1)
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 在新数组末尾添加元素
newElements[len] = e;
// 原子切换引用
setArray(newElements);
return true;
}
}
javapublic E get(int index) {
// 直接访问当前数组,无需同步
return elementAt(getArray(), index);
}
static <E> E elementAt(Object[] a, int index) {
return (E) a[index];
}
javapublic Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
static final class COWIterator<E> implements ListIterator<E> {
// 迭代器创建时的数组快照
private final Object[] snapshot;
private int cursor;
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++];
}
}
java// JDK 17中的锁优化
final transient Object lock = new Object();
由于迭代器使用创建时的快照,会导致:
javaCopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("A");
Iterator<String> it = list.iterator();
list.add("B"); // 在迭代器创建后修改集合
it.forEachRemaining(System.out::print); // 只输出"A"
java// 删除元素时的优化处理
public E remove(int index) {
synchronized (lock) {
// ...
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);
}
// ...
}
}
javaclass SafeCounter {
// ThreadLocal保证每个线程独立计数
private final ThreadLocal<AtomicInteger> threadCount =
ThreadLocal.withInitial(AtomicInteger::new);
// COW List安全收集所有线程的计数
private final CopyOnWriteArrayList<Integer> totalCounts =
new CopyOnWriteArrayList<>();
public void increment() {
threadCount.get().incrementAndGet();
}
public void publish() {
// 线程结束时将计数加入全局列表
totalCounts.add(threadCount.get().get());
threadCount.remove();
}
}
javaclass PerformanceMonitor {
// 线程本地存储监控数据
private final ThreadLocal<PerfData> threadPerfData = ...;
// 全局只读的性能数据快照
private volatile CopyOnWriteArrayList<PerfData> snapshot =
new CopyOnWriteArrayList<>();
// 定期生成快照
public void takeSnapshot() {
List<PerfData> newSnapshot = new ArrayList<>();
for (Thread t : getAllThreads()) {
// 安全获取各线程数据(需线程协作)
PerfData data = getPerfDataSafely(t);
if (data != null) newSnapshot.add(data);
}
snapshot = new CopyOnWriteArrayList<>(newSnapshot);
}
// 分析读取快照(无锁高性能)
public void analyze() {
for (PerfData data : snapshot) {
// 分析处理
}
}
}
java// 观察者模式中的监听器管理
public class EventPublisher {
private final 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);
}
}
}
java// 错误示例:百万级数据集频繁修改
CopyOnWriteArrayList<BigData> hugeList = ...;
// 正确替代:使用ConcurrentHashMap等
ConcurrentMap<Long, BigData> concurrentMap = ...;
java// 写多读少场景优化
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
javapublic void addAllBatch(Collection<? extends E> c) {
synchronized (lock) {
Object[] elements = getArray();
Object[] newElements = Arrays.copyOf(
elements, elements.length + c.size());
int i = elements.length;
for (E e : c) {
newElements[i++] = e;
}
setArray(newElements); // 一次切换
}
}
java// 高频读取场景优化
public class FastReader<E> {
private volatile Object[] currentArray;
public FastReader(CopyOnWriteArrayList<E> list) {
// 保存当前引用
currentArray = list.getArray();
}
public E get(int index) {
// 直接访问本地缓存的数组引用
return (E) currentArray[index];
}
public void refresh(CopyOnWriteArrayList<E> list) {
// 需要时刷新引用
currentArray = list.getArray();
}
}
CopyOnWriteArrayList的核心价值: -️ 安全优先:通过数据复制保证线程安全 -⚡ 读高性能:无锁读操作支持高并发 -📸 迭代安全:快照机制避免ConcurrentModificationException
适用场景抉择:
思考题:
CopyOnWriteArrayList以其独特的设计理念,在特定的并发场景下提供了高效的解决方案。理解其底层实现原理,能帮助我们在复杂的并发环境下做出更合理的技术选型。