简介
CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过数组实现,每次对数组的修改都完全拷贝一份新的数组来修改,修改完了再替换掉老数组,这样保证了只阻塞写操作,不阻塞读操作,实现读写分离。
继承体系
CopyOnWriteArrayList实现了List, RandomAccess, Cloneable, java.io.Serializable等接口。
CopyOnWriteArrayList实现了List,提供了基础的添加、删除、遍历等操作。
CopyOnWriteArrayList实现了RandomAccess,提供了随机访问的能力。
CopyOnWriteArrayList实现了Cloneable,可以被克隆。
CopyOnWriteArrayList实现了Serializable,可以被序列化。
源码解析
属性
/** 用于修改时加锁 */final transient ReentrantLock lock = new ReentrantLock();/** 真正存储元素的地方,只能通过getArray()/setArray()访问 */private transient volatile Object[] array;
(1)lock
用于修改时加锁,使用transient修饰表示不自动序列化。
(2)array
真正存储元素的地方,使用transient修饰表示不自动序列化,使用volatile修饰表示一个线程对这个字段的修改另外一个线程立即可见。
问题:为啥没有size字段?且听后续分解。
CopyOnWriteArrayList()构造方法
创建空数组。
public CopyOnWriteArrayList() { // 所有对array的操作都是通过setArray()和getArray()进行 setArray(new Object[0]);}final void setArray(Object[] a) { array = a;}
CopyOnWriteArrayList(Collection extends E> c)构造方法
如果c是CopyOnWriteArrayList类型,直接把它的数组赋值给当前list的数组,注意这里是浅拷贝,两个集合共用同一个数组。
如果c不是CopyOnWriteArrayList类型,则进行拷贝把c的元素全部拷贝到当前list的数组中。
public CopyOnWriteArrayList(Collection extends E> c) { Object[] elements; if (c.getClass() == CopyOnWriteArrayList.class) // 如果c也是CopyOnWriteArrayList类型 // 那么直接把它的数组拿过来使用 elements = ((CopyOnWriteArrayList>)c).getArray(); else { // 否则调用其toArray()方法将集合元素转化为数组 elements = c.toArray(); // 这里c.toArray()返回的不一定是Object[]类型 // 详细原因见ArrayList里面的分析 if (elements.getClass() != Object[].class) elements = Arrays.copyOf(elements, elements.length, Object[].class); } setArray(elements);}
CopyOnWriteArrayList(E[] toCopyIn)构造方法
把toCopyIn的元素拷贝给当前list的数组。
public CopyOnWriteArrayList(E[] toCopyIn) { setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));}
add(E e)方法
添加一个元素到末尾。
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; setArray(newElements); return true; } finally { // 释放锁 lock.unlock(); }}
(1)加锁;
(2)获取元素数组;
(3)新建一个数组,大小为原数组长度加1,并把原数组元素拷贝到新数组;
(4)把新添加的元素放到新数组的末尾;
(5)把新数组赋值给当前对象的array属性,覆盖原数组;
(6)解锁;
add(int index, E element)方法
添加一个元素在指定索引处。
public void add(int index, E element) { final ReentrantLock lock = this.lock; // 加锁 lock.lock(); try { // 获取旧数组 Object[] elements = getArray(); int len = elements.length; // 检查是否越界, 可以等于len if (index > len || index < 0) throw new IndexOutOfBoundsException("Index: "+index+