CopyOnWriteArrayList写时复制原理
Java JUC 并发包中,关于 List 容器的并发安全的实现类只有一个,就是 CopyOnWriteArrayList。
CopyOnWrite,顾名思义就是写的时候会将共享变量新复制一份出来,具体的原理是
- CopyOnWriteArrayList 内部维护了一个数组成员变量 array
- 所有的读操作都是基于 array 进行的
- 而每次写操作时,都会会将 array 复制一份,然后在新复制处理的数组上执行增加元素的操作,执行完之后再将 array 指向这个新的数组。
CopyOnWriteArrayList的特性
-
所有的读操作都是无锁的
-
写操作是加锁的,只能串行执行
-
读操作和写操作可以并行执行,因为读操作是基于原array,而写操作则是基于新array。
-
同理,迭代器遍历也是线程安全的,因为遍历操作一直都是基于原 array ,而写操作则是基于新 array 。
使用注意事项
-
CopyOnWriteArrayList 仅适用于写操作非常少的场景,因为它的写操作是加锁互斥执行的,且执行时还需要执行拷贝数组操作。
-
CopyOnWriteArrayList 迭代器是只读的,不支持增删改。因为迭代器遍历的仅仅是一个快照,而对快照进行增删改是没有意义的。
部分源码介绍
// get操作是无锁的,getArray方法就是获取当前array数组
public E get(int index) {
return get(getArray(), index);
}
// add方法是加锁互斥执行的
// 且会拷贝一份新数组newElements
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();
}
}
// 获取的迭代器也是基于当前数组array
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
// 迭代器不支持增删改操作,因为迭代器遍历的仅仅是一个快照,而对快照进行增删改是没有意义的。
static final class COWIterator<E> implements ListIterator<E> {
public void remove() {
throw new UnsupportedOperationException();
}
public void set(E e) {
throw new UnsupportedOperationException();
}
public void add(E e) {
throw new UnsupportedOperationException();
}
}
参考资料: