CopyOnWriteArrayList源码分析
前言:
当我们想要用ArrayList,又想要保证线程安全的时候,可以考虑使用CopyOnWriteArrayList这个类。因为如果使用Vector的话,虽然可以保证线程安全,但是因为在Vector里面是用synchronized修饰的,所以开销会比较大。因此考虑使用CopyOnWriteArrayList。
一.概述
CopyOnWriteArrayList是concurrent 并发包下的一个类,由名称可以看出他是基于数据的一个链表,这个链表在进行写操作的时候回进行copy,接下来就通过源码来看看这个类。
二.源码分析
1.属性:
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
/**
* Sets the array.
*/
final void setArray(Object[] a) {
array = a;
}
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
首先先看CopyOnWriteArrayList这个类的相关属性,可以看到他实现了RandomAccess接口,所以支持随机访问的。然后定义了一个不被序列化的ReentranLock实例对象,通过这个对象实现了加锁同步的操作,关于这个ReentranLock这个类可以参照这个:https://blog.csdn.net/striveb/article/details/83421107,此外,还有一个不被序列化并且具有可见性的数组,在构造方法里面生成了这样的一个空数组。
2.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();
}
}
由上面源码可以看出,添加元素,即写操作是通过ReentranLock加锁实现的。由上面属性可知,写操作是先复制一个数组,然后在这个数组上进行新增元素,写操作结束之后将原始数组指向新的复制数组。
3.get方法
private E get(Object[] a, int index) {
return (E) a[index];
}
由上面源码可以看出,读操作就是直接返回数组对应的值,并没有加锁。
4.set方法
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantic
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
由上面源码可知,set方法也是通过加锁实现修改元素的,其中也是先复制了数组,跟add方法基本相似。
三、使用场景
由上面的分析可知,CopyOnWriteArrayList在添加元素和修改元素的操作上都进行了加锁,所以在两个操作上是会消耗一定的性能的,因此这个类主要使用在读多写少的场景上。