CopyOnWriteArrayList
1.介绍
CopyOnWriteArrayList可以理解为一个线程安全的List,它的实现原理是:在写入操作时,进行一次自我复制。也就是说,对于CopyOnWriteArrayList的读操作,不加任何同步处理;当需要进行修改操作时,对原有的数据进行一次复制,将修改的内容写入副本中。写完之后,再用修改后的副本替换原来的数据。这样一来,写操作与读操作之间互不影响。CopyOnWriteArrayList的读-读操作和读-写操作之间都是可以并发执行的,只有在写-写操作之间才互斥。
2.代码分析
首先看 private volatile transient Object[] array;有关读取的操作,以get()方法为例:
public E get(int index) {
return get(getArray(), index);
}
final Object[] getArray() {
return array;
}
private volatile transient Object[] array;
CopyOnWriteArrayList内部使用一个Object[] array数组保存数据,get()方法直接获取array数组index下标处的元素,没有采用任何同步控制和锁操作,由于内部数组不会发生修改,只能被替换,因此可以保证线程安全。
下面再看修改操作的代码,以add()方法为例
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();
}
}
可以看出,整个修改操作都是在副本数组上进行的,对读取操作不会产生影响。而且,由于内部数组使用volatile关键字修饰,所以数组修改后可以立即被读线程察觉。
3.适用场景
对于读操作远远大于写操作的情况,使用CopyOnWriteArrayList可以很大程度地提高效率。但是如果写操作较多,CopyOnWriteArrayList会频繁地进行内部数组的复制,效率较低。