CopyOnWriteArrayList
类图
CopyOnWriteArrayList
类的思想
CopyOnWrite
简称 COW
,根据名字来看就是写入时复制。意思就是大家共同去访问一个资源,如果有人想要去修改这个资源的时候,就需要复制一个副本,去修改这个副本,而对于其他人来说访问的资源还是原来的,不会发生变化
CopyOnWriteArrayList
源码
CopyOnWriteArrayList
的变量
CopyOnWriteArrayList
底层是也是由数组实现的。本文我们只解读添加元素和读取元素的区别,删除修改元素原理和添加元素差不多,操作时都需要进行加锁,而读操作不会加锁
// 独占锁
final transient ReentrantLock lock = new ReentrantLock();
// 存放元素的数组
private transient volatile Object[] array;
我们仔细来分析一下上面两个属性,这两个思想是 CopyOnWriteArrayList
的核心
lock:ReentrantLock
,独占锁,多线程运行的情况下,只有一个线程会获得这个锁,只有释放锁后其他线程才能获得array
:存放数据的数组,关键是被volatile
修饰了,被volatile
修饰,就保证了可见性,也就是一个线程修改后,其他线程立即可见
CopyOnWriteArrayList
的初始化
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
if (c.getClass() == CopyOnWriteArrayList.class)
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
elements = c.toArray();
if (c.getClass() != ArrayList.class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
setArray(elements);
}
public CopyOnWriteArrayList(E[] toCopyIn) {
setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}
final void setArray(Object[] a) {
array = a;
}
CopyOnWriteArrayList
添加元素(独占锁 ReentrantLock
)
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(); // 解锁
}
}
添加元素的步骤如下:
- 获得独占锁,将添加功能加锁
- 获取原来的数组,并得到其长度
- 创建一个长度为原来数组长度
+1
的数组,并拷贝原来的元素给新数组 - 追加元素到新数组末尾
- 指向新数组
- 释放锁
这个过程是线程安全的,COW
的核心思想就是每次修改的时候拷贝一个新的资源去修改,add()
方法再拷贝新资源的时候将数组容量 +1
,这样虽然每次添加元素都会浪费一定的空间,但是数组的长度正好是元素的长度,也在一定程度上节省了扩容的开销
CopyOnWriteArrayList
获取元素
public E get(int index) {
return get(getArray(), index);
}
final Object[] getArray() {
return array;
}
private E get(Object[] a, int index) {
return (E) a[index];
}
读操作是天然安全的操作,而且数组本身会进行检查越界问题,因此获取元素的方法很简单,只是根据索引获取该元素
CopyOnWriteArrayList
和 Vector
小结
Vector
和CopyOnWriteArrayList
都是线程安全的List
,底层都是数组实现的Vector
的每个方法都进行了加锁,而CopyOnWriteArrayList
的读操作是不加锁的,因此CopyOnWriteArrayList
的读性能远高于Vector
Vector
每次扩容的大小都是原来数组大小的2
倍,而CopyOnWriteArrayList
不需要扩容,通过COW
思想就能使数组容量满足要求- 两个集合都实现了
RandomAccess
接口,支持随机读取,因此更加推荐使用for
循环进行遍历。在开发中,读操作会远远多于其他操作,因此使CopyOnWriteArrayList
集合效率更高