概述
CopyOnWriteArrayList是一个以数组和可重入锁实现的List(线程安全),每次对数组的修改时都先拷贝一份新的数组来操作,修改完成后在覆盖旧数组,保证只阻塞写操作,不阻塞读操作,实现读写分离
UML类图
CopyOnWriteArrayList实现了List, RandomAccess, Cloneable, java.io.Serializable等接口;
CopyOnWriteArrayList实现了List,提供了基础的添加、删除、遍历等操作;
CopyOnWriteArrayList实现了RandomAccess,提供了随机访问的能力;
CopyOnWriteArrayList实现了Cloneable,可以被克隆;
CopyOnWriteArrayList实现了Serializable,可以被序列化;
源码解析
属性
/** 可重入锁,写操作时应用 */
final transient ReentrantLock lock = new ReentrantLock();
/** volatile修饰数组,保证变量内存可见性. */
private transient volatile Object[] array;
构造器
/**
* 创建空数组.
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
/** param:collection */
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
elements = c.toArray();
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements);
}
/** 拷贝新数组 */
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
/** 取值. */
final Object[] getArray() {
return array;
}
/**
* 设值.
*/
final void setArray(Object[] a) {
array = a;
}
方法
add(E e)
功能
添加元素到数组末尾,平均时间复杂度O(n);
流程
- 加锁
- 拷贝新数组
- 末尾加元素
- 覆盖旧数组
- 解锁
源码
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();
}
}
add(int index, E e)
功能
添加元素到数组指定位置,平均时间复杂度O(n);
流程
- 加锁
- 数组越界判断
- 分段拷贝数据到新数组并在指定节点加入元素
- 覆盖旧数组
- 解锁
源码
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();
}
}
get(int index)
功能
根据指定下标返回数据,平均时间复杂度O(1);
源码
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
remove(int index)
功能
功能:删除并返回指定下标的数据,平均时间复杂度为O(n)
流程
- 加锁
- 获取旧数组并删除指定元素
- 分段拷贝数据至新数组并清除最后一位元素
- 解锁
源码
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)
//如果要删除的元素刚好是最后一个,拷贝新数组-1个元素会旧数组
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();
}
}
总结
CopyOnWriteArrayList使用ReentrantLock重入锁,保证线程安全
CopyOnWriteArrayList写操作都要先拷贝一份新数组,在新数组中修改完覆盖旧数组,平均时间复杂度O(n)
CopyOnWriteArrayList支持随机访问,平均时间复杂度O(1)
CopyOnWriteArrayList采用读写分离的思想,写时加锁,读时不加锁,适合读多写少的场景
CopyOnWriteArrayList可保证最终结果一致性,不保证实时一致性