一、介绍
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
CopyOnWriteArrayList继承了List接口,类如其名,再写时复制,以保证线程安全。
CopyOnWriteArrayList的成员变量:
final transient ReentrantLock lock = new ReentrantLock();
使用了ReentrantLock,每次写操作时获取锁。
private transient volatile Object[] array;
array是CopyOnWriteArrayList里面保存的数据,transient修饰就是序列化时数据不进行序列化,volatile修饰则保障了内存可见性。
二、函数
2.1 构造函数
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
final void setArray(Object[] a) {
array = a;
}
构造函数时创建一个空的Object类型的数组,array指向这个空数组。
2.2 add()
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;
//将array指向新数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
写之前总是先获取锁,然后创建一个新数组(包含原数组的内容和要添加的新元素),再将array指向新的数组。所以写操作是线程安全的,但是这样每次写的时候都要创建新数组,复制原数组的数据,是很耗时的。
2.3 get()
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
get就是直接用数组下标的方式获取,没有通过锁的方式去获取,所以读是不会线程阻塞的。
三、总结
CopyOnWriteArrayList是线程安全的,但是每次修改操作时都伴随大量的复制,效率会比较低下。所以CopyOnWriteArrayList适用于读远远大于写的并发场景下。