JDK 8 CopyOnWriteArrayList 源码详解(详细注释版)
1. 类定义和基本属性
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 序列化版本号
private static final long serialVersionUID = 8673264195747942595L;
/**
* 用于保证可见性的锁对象
* 使用ReentrantLock实现线程安全的写操作
* 通过加锁确保同一时刻只有一个线程可以修改数组
*/
final transient ReentrantLock lock = new ReentrantLock();
/**
* 存储元素的数组,使用volatile保证内存可见性
* 当数组被修改时,会创建新的数组并更新这个引用
* volatile确保所有线程都能看到最新的数组引用
*/
private transient volatile Object[] array;
/**
* 获取当前数组的引用
* 由于array字段是volatile的,这个方法保证返回最新的数组引用
* 读操作不需要加锁,提高了并发读取的性能
* @return 当前的数组引用
*/
final Object[] getArray() {
return array;
}
/**
* 设置数组引用
* 通过volatile写保证新数组对所有线程可见
* 这是CopyOnWrite机制的核心:写时复制
* @param a 新的数组引用
*/
final void setArray(Object[] a) {
array = a;
}
/**
* 默认构造方法
* 创建一个空的CopyOnWriteArrayList
* 初始数组为空数组
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
/**
* 指定初始集合的构造方法
* 创建一个包含指定集合元素的CopyOnWriteArrayList
* @param c 包含初始元素的集合
* @throws NullPointerException 如果c为null
*/
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
// 如果参数也是CopyOnWriteArrayList,直接复用其数组
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
// 否则将集合转换为数组
elements = c.toArray();
// 如果toArray()返回的不是Object[]类型,需要转换
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements); // 设置初始数组
}
/**
* 指定数组的构造方法(包私有)
* 主要用于内部使用,创建指定数组的CopyOnWriteArrayList
* @param toCopyIn 要复制的数组
*/
CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
2. 核心读取方法(详细注释)
/**
* 获取指定位置的元素
* 这是读操作,完全无锁,性能很高
* 由于使用了volatile语义,保证获取到最新的数组引用
* @param index 元素的索引位置
* @return 指定位置的元素
* @throws IndexOutOfBoundsException 如果索引越界
*
* 读操作的特点:
* 1. 无锁操作,性能极高
* 2. 可能读取到"过时"的数据(弱一致性)
* 3. 不会阻塞写操作
* 4. 保证不会抛出ConcurrentModificationException
*/
public E get(int index) {
return get(getArray(), index); // 调用私有方法获取元素
}
/**
* 私有的获取元素方法
* 通过数组引用和索引获取元素,避免重复获取数组引用
* @param a 数组引用
* @param index 元素索引
* @return 指定位置的元素
*/
private E get(Object[] a, int index) {
// 直接通过数组索引访问元素,时间复杂度O(1)
// 类型转换是安全的,因为数组中存储的都是E类型的元素
return (E) a[index];
}
/**
* 返回列表中元素的数量
* 通过获取当前数组的长度来确定元素数量
* @return 元素数量
*
* size()操作的特点:
* 1. 无锁操作,性能很高
* 2. 返回的是调用时刻的快照数量
* 3. 在并发环境下可能不是最新的数量
*/
public int size() {
// 直接返回数组长度,时间复杂度O(1)
return getArray().length;
}
/**
* 判断列表是否为空
* 通过检查数组长度来判断
* @return 如果列表为空返回true,否则返回false
*/
public boolean isEmpty() {
// 直接检查数组长度,时间复杂度O(1)
return size() == 0;
}
/**
* 判断列表是否包含指定元素
* 通过遍历数组来查找元素
* @param o 要查找的元素
* @return 如果包含返回true,否则返回false
*/
public boolean contains(Object o) {
Object[] elements = getArray(); // 获取当前数组
return indexOf(o, elements, 0, elements.length) >= 0;
}
/**
* 返回指定元素第一次出现的索引
* @param o 要查找的元素
* @return 元素第一次出现的索引,如果不存在返回-1
*/
public int indexOf(Object o) {
Object[] elements = getArray(); // 获取当前数组
return indexOf(o, elements, 0, elements.length);
}
/**
* 从指定位置开始查找元素第一次出现的索引
* @param o 要查找的元素
* @param index 开始查找的位置
* @return 元素第一次出现的索引,如果不存在返回-1
*/
public int indexOf(E o, int index) {
Object[] elements = getArray(); // 获取当前数组
return indexOf(o, elements, index, elements.length);
}
/**
* 在指定范围内查找元素第一次出现的索引
* 这是核心的查找实现方法
* @param o 要查找的元素
* @param elements 要搜索的数组
* @param index 开始查找的位置
* @param end 查找结束的位置(不包含)
* @return 元素第一次出现的索引,如果不存在返回-1
*/
private int indexOf(Object o, Object[] elements, int index, int end) {
if (o == null) {
// 处理null元素的查找
for (int i = index; i < end; i++)
if (elements[i] == null)
return i;
} else {
// 处理非null元素的查找
for (int i = index; i < end; i++)
if (o.equals(elements[i]))
return i;
}
return -1; // 没有找到元素
}
/**
* 返回指定元素最后一次出现的索引
* @param o 要查找的元素
* @return 元素最后一次出现的索引,如果不存在返回-1
*/
public int lastIndexOf(Object o) {
Object[] elements = getArray(); // 获取当前数组
return lastIndexOf(o, elements, elements.length - 1);
}
/**
* 从指定位置开始向前查找元素最后一次出现的索引
* @param o 要查找的元素
* @param index 开始查找的位置
* @return 元素最后一次出现的索引,如果不存在返回-1
*/
public int lastIndexOf(E o, int index) {
Object[] elements = getArray(); // 获取当前数组
return lastIndexOf(o, elements, index);
}
/**
* 向前查找元素最后一次出现的索引
* @param o 要查找的元素
* @param elements 要搜索的数组
* @param index 开始查找的位置
* @return 元素最后一次出现的索引,如果不存在返回-1
*/
private int lastIndexOf(Object o, Object[] elements, int index) {
if (index < 0) {
return -1; // 索引越界
}
if (o == null) {
// 处理null元素的查找
for (int i = index; i >= 0; i--)
if (elements[i] == null)
return i;
} else {
// 处理非null元素的查找
for (int i = index; i >= 0; i--)
if (o.equals(elements[i]))
return i;
}
return -1; // 没有找到元素
}
/**
* 检查给定索引是否在有效范围内
* @param index 要检查的索引
* @param size 数组大小
*/
private static void checkIndex(int index, int size) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
3. 核心写入方法(详细注释)
/**
* 在列表末尾添加元素
* 这是写操作,需要加锁保证线程安全
* 采用CopyOnWrite机制,创建新数组并复制原有元素
* @param e 要添加的元素
* @return 总是返回true
* @throws NullPointerException 如果e为null
*
* 添加操作的特点:
* 1. 加锁保证线程安全
* 2. CopyOnWrite机制,不影响正在进行的读操作
* 3. 读操作可能看不到新添加的元素(弱一致性)
* 4. 内存开销较大,每次写操作都要复制整个数组
*/
public boolean add(E e) {
// 获取锁,确保同一时刻只有一个线程可以修改数组
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 获取当前数组
Object[] elements = getArray();
int len = elements.length;
// 创建新数组,长度比原数组多1
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 在新数组末尾添加元素
newElements[len] = e;
// 更新数组引用,volatile写保证对其他线程可见
setArray(newElements);
return true;
} finally {
// 释放锁,确保锁一定会被释放
lock.unlock();
}
}
/**
* 在指定位置插入元素
* 需要将插入位置后的所有元素向后移动一位
* @param index 插入位置
* @param element 要插入的元素
* @throws IndexOutOfBoundsException 如果索引越界
* @throws NullPointerException 如果element为null
*/
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 检查索引是否有效
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: " + index +
", Size: " + len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
// 如果在末尾插入,直接复制数组
newElements = Arrays.copyOf(elements, len + 1);
else {
// 如果在中间插入,需要分段复制
newElements = new Object[len + 1];
// 复制插入位置之前的元素
System.arraycopy(elements, 0, newElements, 0, index);
// 复制插入位置之后的元素
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
// 在指定位置插入新元素
newElements[index] = element;
// 更新数组引用
setArray(newElements);
} finally {
lock.unlock();
}
}
/**
* 移除指定位置的元素
* @param index 要移除元素的索引
* @return 被移除的元素
* @throws IndexOutOfBoundsException 如果索引越界
*/
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 检查索引是否有效
E oldValue = get(elements, index);
int numMoved = len - index - 1;
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 {
lock.unlock();
}
}
/**
* 移除第一次出现的指定元素
* @param o 要移除的元素
* @return 如果成功移除返回true,否则返回false
*/
public boolean remove(Object o) {
Object[] snapshot = getArray(); // 获取当前数组快照
int index = indexOf(o, snapshot, 0, snapshot.length);
return (index < 0) ? false : remove(o, snapshot, index);
}
/**
* 移除指定元素的核心实现
* @param o 要移除的元素
* @param snapshot 数组快照
* @param index 要移除元素的索引
* @return 如果成功移除返回true,否则返回false
*/
private boolean remove(Object o, Object[] snapshot, int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] current = getArray();
int len = current.length;
if (snapshot != current) findIndex: {
// 如果数组在获取快照后发生了变化,需要重新查找索引
int prefix = Math.min(index, len);
for (int i = 0; i < prefix; i++) {
if (current[i] != snapshot[i] && eq(o, current[i])) {
index = i;
break findIndex;
}
}
if (index >= len)
return false;
Object[] temp = new Object[len - 1];
System.arraycopy(current, 0, temp, 0, index);
System.arraycopy(current, index + 1, temp, index,
len - index - 1);
setArray(temp);
return true;
} else {
// 如果数组没有变化,直接移除
Object[] newElements = new Object[len - 1];
System.arraycopy(current, 0, newElements, 0, index);
System.arraycopy(current, index + 1, newElements, index,
len - index - 1);
setArray(newElements);
return true;
}
} finally {
lock.unlock();
}
}
/**
* 比较两个对象是否相等
* 处理null值的情况
* @param o1 第一个对象
* @param o2 第二个对象
* @return 如果相等返回true,否则返回false
*/
private static boolean eq(Object o1, Object o2) {
return (o1 == null) ? o2 == null : o1.equals(o2);
}
/**
* 移除指定范围内的元素
* @param fromIndex 开始索引(包含)
* @param toIndex 结束索引(不包含)
* @throws IndexOutOfBoundsException 如果索引范围无效
*/
void removeRange(int fromIndex, int toIndex) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
int newlen = len - (toIndex - fromIndex);
int numMoved = len - toIndex;
if (numMoved == 0)
setArray(Arrays.copyOf(elements, newlen));
else {
Object[] newElements = new Object[newlen];
System.arraycopy(elements, 0, newElements, 0, fromIndex);
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
setArray(newElements);
}
} finally {
lock.unlock();
}
}
/**
* 替换指定位置的元素
* @param index 要替换元素的索引
* @param element 新元素
* @return 被替换的旧元素
* @throws IndexOutOfBoundsException 如果索引越界
*/
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// 如果新旧元素相同,不需要创建新数组
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
/**
* 清空列表中的所有元素
* 将数组引用设置为空数组
*/
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
setArray(new Object[0]);
} finally {
lock.unlock();
}
}
/**
* 添加指定集合中的所有元素
* @param c 包含要添加元素的集合
* @return 如果列表发生了变化返回true,否则返回false
* @throws NullPointerException 如果c为null
*/
public boolean addAll(Collection<? extends E> c) {
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
if (cs.length == 0)
return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (len == 0 && cs.getClass() == Object[].class)
setArray(cs);
else {
Object[] newElements = Arrays.copyOf(elements, len + cs.length);
System.arraycopy(cs, 0, newElements, len, cs.length);
setArray(newElements);
}
return true;
} finally {
lock.unlock();
}
}
/**
* 从指定位置开始添加指定集合中的所有元素
* @param index 开始添加的位置
* @param c 包含要添加元素的集合
* @return 如果列表发生了变化返回true,否则返回false
* @throws IndexOutOfBoundsException 如果索引越界
* @throws NullPointerException 如果c为null
*/
public boolean addAll(int index, Collection<? extends E> c) {
Object[] cs = c.toArray();
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: " + index +
", Size: " + len);
if (cs.length == 0)
return false;
int numMoved = len - index;
Object[] newElements;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + cs.length);
else {
newElements = new Object[len + cs.length];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + cs.length,
numMoved);
}
System.arraycopy(cs, 0, newElements, index, cs.length);
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
4. 迭代器实现(详细注释)
/**
* 返回列表的迭代器
* 返回的迭代器是"弱一致性"的,不会抛出ConcurrentModificationException
* @return 列表的迭代器
*
* 迭代器的特点:
* 1. 弱一致性:反映创建迭代器时的状态快照
* 2. 不会抛出ConcurrentModificationException
* 3. 在迭代过程中对列表的修改不会影响当前迭代器
* 4. 内存占用:持有创建时的数组引用
*/
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
/**
* 返回从指定位置开始的列表迭代器
* @param index 开始位置
* @return 列表迭代器
* @throws IndexOutOfBoundsException 如果索引越界
*/
public ListIterator<E> listIterator(int index) {
Object[] elements = getArray();
if (index < 0 || index > elements.length)
throw new IndexOutOfBoundsException("Index: " + index);
return new COWIterator<E>(elements, index);
}
/**
* CopyOnWriteArrayList的迭代器实现
* 这是一个静态内部类,不持有对外部类的引用
*/
static final class COWIterator<E> implements ListIterator<E> {
/**
* 创建迭代器时的数组快照
* 保证迭代过程中数组不会发生变化
*/
private final Object[] snapshot;
/**
* 当前迭代位置的索引
*/
private int cursor;
/**
* 构造方法
* @param elements 数组快照
* @param initialCursor 初始游标位置
*/
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
/**
* 判断是否还有下一个元素
* @return 如果还有下一个元素返回true,否则返回false
*/
public boolean hasNext() {
return cursor < snapshot.length;
}
/**
* 获取下一个元素
* @return 下一个元素
* @throws NoSuchElementException 如果没有下一个元素
*/
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
/**
* 判断是否还有上一个元素
* @return 如果还有上一个元素返回true,否则返回false
*/
public boolean hasPrevious() {
return cursor > 0;
}
/**
* 获取上一个元素
* @return 上一个元素
* @throws NoSuchElementException 如果没有上一个元素
*/
public E previous() {
if (! hasPrevious())
throw new NoSuchElementException();
return (E) snapshot[--cursor];
}
/**
* 返回下一个元素的索引
* @return 下一个元素的索引
*/
public int nextIndex() {
return cursor;
}
/**
* 返回上一个元素的索引
* @return 上一个元素的索引
*/
public int previousIndex() {
return cursor - 1;
}
/**
* 移除元素(不支持)
* CopyOnWriteArrayList的迭代器不支持修改操作
* @throws UnsupportedOperationException 总是抛出此异常
*/
public void remove() {
throw new UnsupportedOperationException();
}
/**
* 设置元素(不支持)
* @param e 要设置的元素
* @throws UnsupportedOperationException 总是抛出此异常
*/
public void set(E e) {
throw new UnsupportedOperationException();
}
/**
* 添加元素(不支持)
* @param e 要添加的元素
* @throws UnsupportedOperationException 总是抛出此异常
*/
public void add(E e) {
throw new UnsupportedOperationException();
}
/**
* 迭代器的弱一致性特点:
* 1. 持有创建时的数组快照,不受后续修改影响
* 2. 不会抛出ConcurrentModificationException
* 3. 可能遍历到"过时"的数据
* 4. 保证遍历过程的安全性和一致性
*/
}
5. 其他重要方法(详细注释)
/**
* 转换为数组
* 返回包含列表所有元素的数组
* @return 包含所有元素的数组
*
* toArray()方法的特点:
* 1. 返回创建时的数组快照
* 2. 不受后续修改影响
* 3. 线程安全,无锁操作
*/
public Object[] toArray() {
Object[] elements = getArray();
return Arrays.copyOf(elements, elements.length);
}
/**
* 转换为指定类型的数组
* @param a 指定类型的数组
* @return 转换后的数组
* @throws ArrayStoreException 如果数组类型不兼容
* @throws NullPointerException 如果a为null
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
Object[] elements = getArray();
int len = elements.length;
if (a.length < len)
return (T[]) Arrays.copyOf(elements, len, a.getClass());
else {
System.arraycopy(elements, 0, a, 0, len);
if (a.length > len)
a[len] = null;
return a;
}
}
/**
* 判断两个列表是否相等
* @param o 要比较的对象
* @return 如果相等返回true,否则返回false
*/
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
List<?> list = (List<?>)(o);
Iterator<?> it = iterator();
Iterator<?> oit = list.iterator();
while (it.hasNext() && oit.hasNext()) {
if (!eq(it.next(), oit.next()))
return false;
}
return !(it.hasNext() || oit.hasNext());
}
/**
* 计算哈希码
* @return 列表的哈希码
*/
public int hashCode() {
int hashCode = 1;
Object[] elements = getArray();
for (Object element : elements) {
hashCode = 31 * hashCode + (element == null ? 0 : element.hashCode());
}
return hashCode;
}
/**
* 返回列表的字符串表示
* @return 列表的字符串表示
*/
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
/**
* 克隆列表
* @return 克隆后的列表
*/
public Object clone() {
try {
@SuppressWarnings("unchecked")
CopyOnWriteArrayList<E> clone =
(CopyOnWriteArrayList<E>) super.clone();
clone.initializationLock = new Object();
clone.lock = new ReentrantLock();
return clone;
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
}
6. CopyOnWriteArrayList 的特点分析
核心设计理念:
/**
* CopyOnWriteArrayList的核心设计思想:
*
* 1. Copy-On-Write(写时复制)机制:
* - 读操作无锁,直接访问数组
* - 写操作加锁,创建新数组并复制原有数据
* - 更新数组引用,利用volatile保证可见性
*
* 2. 弱一致性迭代器:
* - 迭代器持有创建时的数组快照
* - 不会抛出ConcurrentModificationException
* - 可能遍历到"过时"的数据
*
* 3. 读写分离:
* - 读操作和写操作互不影响
* - 读操作性能极高,无锁开销
* - 写操作保证原子性和一致性
*
* 4. 内存语义:
* - 使用volatile保证数组引用的可见性
* - 使用ReentrantLock保证写操作的原子性
* - 通过内存屏障确保操作的有序性
*
* 适用场景:
* - 读操作远多于写操作的场景
* - 对一致性要求不严格的场景
* - 需要频繁遍历的场景
* - 不支持元素修改的场景
*/
性能特征分析:
/**
* CopyOnWriteArrayList的性能特征:
*
* 时间复杂度:
* - get(index): O(1) - 直接数组访问
* - size(): O(1) - 直接返回数组长度
* - contains(): O(n) - 需要遍历数组
* - add(element): O(n) - 需要复制整个数组
* - add(index, element): O(n) - 需要复制和移动元素
* - remove(index): O(n) - 需要复制数组
* - iterator(): O(1) - 创建快照引用
*
* 空间复杂度:
* - O(n) 基本存储空间
* - 写操作时需要额外O(n)空间用于复制数组
* - 迭代器持有数组快照,可能增加内存占用
*
* 并发特性:
* - 读操作完全无锁,性能极高
* - 写操作使用独占锁,保证线程安全
* - 读写操作互不影响
* - 弱一致性:读操作可能看不到最新的写操作结果
* - 迭代器安全:不会抛出ConcurrentModificationException
*
* 内存开销:
* - 每次写操作都要复制整个数组
* - 在写操作频繁的场景下内存开销很大
* - 迭代器持有数组快照,可能阻止垃圾回收
*/
7. 使用示例和最佳实践
/**
* 使用示例:
*
* // 基本使用
* CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
*
* // 添加元素
* list.add("element1");
* list.add("element2");
* list.add("element3");
*
* // 读取元素
* String first = list.get(0);
* int size = list.size();
* boolean contains = list.contains("element2");
*
* // 遍历元素(线程安全)
* for (String element : list) {
* System.out.println(element);
* }
*
* // 在多线程环境中使用
* ExecutorService executor = Executors.newFixedThreadPool(10);
*
* // 读线程
* for (int i = 0; i < 5; i++) {
* executor.submit(() -> {
* for (int j = 0; j < 1000; j++) {
* // 读操作完全无锁,性能很高
* int size = list.size();
* if (size > 0) {
* String element = list.get(0);
* }
* }
* });
* }
*
* // 写线程
* for (int i = 0; i < 5; i++) {
* executor.submit(() -> {
* for (int j = 0; j < 100; j++) {
* // 写操作加锁,保证线程安全
* list.add("thread-" + Thread.currentThread().getId() + "-item-" + j);
* }
* });
* }
*
* 最佳实践:
*
* 1. 适合读多写少的场景:
* // 好的使用场景
* CopyOnWriteArrayList<String> cache = new CopyOnWriteArrayList<>();
* // 频繁读取,偶尔更新
* String config = cache.get(0); // 高频操作
* cache.add("newConfig"); // 低频操作
*
* 2. 避免频繁的写操作:
* // 不好的使用场景
* CopyOnWriteArrayList<Integer> numbers = new CopyOnWriteArrayList<>();
* // 频繁写入会导致大量数组复制,性能很差
* for (int i = 0; i < 10000; i++) {
* numbers.add(i); // 每次都要复制整个数组
* }
*
* 3. 合理使用迭代器:
* // 迭代器是弱一致性的
* Iterator<String> it = list.iterator();
* while (it.hasNext()) {
* String element = it.next();
* // 在迭代过程中其他线程的修改不会影响当前迭代器
* System.out.println(element);
* }
*
* 4. 注意内存使用:
* // 大列表的频繁写入会消耗大量内存
* CopyOnWriteArrayList<LargeObject> largeList = new CopyOnWriteArrayList<>();
* // 考虑使用其他数据结构或分批处理
*
* 5. 避免在迭代过程中进行修改检查:
* // 不需要检查ConcurrentModificationException
* for (String element : list) {
* // 不会抛出ConcurrentModificationException
* System.out.println(element);
* }
*
* 6. 合理设置初始容量:
* // 如果知道大致大小,可以预先设置
* Collection<String> initialData = getInitialData();
* CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(initialData);
*/
8. 与其他并发集合的比较
/**
* CopyOnWriteArrayList vs ArrayList vs Vector vs Collections.synchronizedList:
*
* CopyOnWriteArrayList:
* - 读操作无锁,写操作加锁
* - 弱一致性迭代器
* - 写时复制机制
* - 适合读多写少场景
* - 内存开销大
*
* ArrayList:
* - 非线程安全
* - 读写都无锁
* - 快速失败迭代器
* - 需要外部同步
* - 内存开销小
*
* Vector:
* - 所有操作都加锁
* - 快速失败迭代器
* - 性能较差
* - 已被废弃
*
* Collections.synchronizedList:
* - 所有操作都加锁
* - 快速失败迭代器(需要手动同步)
* - 性能一般
* - 需要手动同步迭代器
*
* 选择建议:
* - 读多写少且对一致性要求不高:CopyOnWriteArrayList
* - 单线程使用:ArrayList
* - 简单同步需求:Collections.synchronizedList
* - 已有遗留代码:Vector(但建议迁移)
*/
9. 总结
CopyOnWriteArrayList 的核心特性:
-
写时复制机制:
- 读操作无锁,性能极高
- 写操作创建新数组,保证线程安全
- 通过volatile保证数组引用的可见性
-
弱一致性:
- 迭代器持有创建时的快照
- 不会抛出ConcurrentModificationException
- 读操作可能看不到最新的写操作结果
-
读写分离:
- 读操作和写操作互不影响
- 支持高并发读取
- 写操作保证原子性
-
内存语义:
- 使用ReentrantLock保证写操作的原子性
- 使用volatile保证可见性
- 通过内存屏障确保有序性
适用场景:
- 读操作远多于写操作的场景
- 需要频繁遍历但很少修改的列表
- 对一致性要求不严格的场景
- 不支持元素修改的场景
- 需要线程安全但不想使用重量级锁的场景
注意事项:
- 写操作频繁时内存开销很大
- 每次写操作都要复制整个数组
- 迭代器持有数组快照,可能阻止垃圾回收
- 不支持在迭代过程中修改列表
- 对于大列表,写操作性能较差
性能优化建议:
- 评估读写比例,确保读操作远多于写操作
- 避免频繁的写操作
- 合理设置初始容量
- 考虑内存使用情况
- 在适当的时候考虑其他并发数据结构

1601

被折叠的 条评论
为什么被折叠?



