1、背景
java诞生之初 就有线程安全的Vector,但Vector对读写都是通过synchronized关键字来同步的,性能并不好
且Vector每次扩容是原来的1倍,存在内存浪费的可能。
对于线程安全的List JDK提供了CopyOnWriteArrayList
2、原理
2.1 CopyOnWriteArrayList 使用写时复制技术,读写分离
对于写操作,会使用ReentrantLock加锁,对底层数组拷贝一份,进行修改,修改完后再覆盖原来的数组
2.2 底层使用数组
private transient volatile Object[] array;
数据使用volatile修饰,故能保证可见性,存在happens-before关系,使得能读到最新的数据
2.3 使用ReentrantLock修改
所有的写操作 读会加锁保证线程安全
但读操作是没有任何锁的,故修改过程中的数据,此时另一个线程是读不到的。
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();
}
}
3、迭代
CopyOnWriteArrayList 迭代时安全失败的,不允许在迭代过程中删除元素,如下会抛出异常
/** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code remove} * is not supported by this iterator. */ public void remove() { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code set} * is not supported by this iterator. */ public void set(E e) { throw new UnsupportedOperationException(); } /** * Not supported. Always throws UnsupportedOperationException. * @throws UnsupportedOperationException always; {@code add} * is not supported by this iterator. */ public void add(E e) { throw new UnsupportedOperationException(); }
创建迭代时,引用了原数组,故迭代过程中如何修改了CopyOnWriteArrayList,
迭代也不会感知。还是对修改前的数据进行访问(注意这是针对数据元素的增删而言,如何存放对象,对象内部的变化还是有影响的)
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
4、使用场景
缺点:
a. 每次修改都会拷贝数组 占用内存 写性能不高
b.修改的过程中 数据依赖读的是老的数据 ,只能保证最终一致 不能保证强一致性
使用场景
1、多线程 读多写少。
2、不要求数据强一致性。
否则推荐使用Collections.synchronziedList