CopyOnWriteArrayList是一个线程安全且在读操作时候无锁的ArrayList,其具体实现如下:
首先在CopyOnWriteArrayList内部定义了一个private类型的数组,并提供getter setter方法,不过需要注意的是该对象数组是被volatile关键字修饰的(关于volatile关键字可以参考我的博客“关于volatile的使用”一文),和ArrayList不同的是这里创建一个大小为0的数组,源码如下:
transient final ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array;
/**
* Creates an empty list.
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
然后我们来看看其add,remove等方法的实现
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();
}
}
这里使用了ReentrantLock首先调用了lock方法,然后获取到对象数组,再调用Arrays.copyOf(elements,len+1)复制了一个长度加1的数组,紧接着把新增元素e添加到数组最后
一个位置上,再调用setArray方法将array引用的对象置换成新的对象,最后再释放锁,整个新增操作就完成了,这里有一个疑惑就是作者为什么在复制数组的时候并没有选择
System.arraycopy,而使用的是Arrays.copyOf方法呢。下面是其remove方法的实现
public boolean remove(Object o) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (len != 0) {
// Copy while searching for element to remove
// This wins in the normal case of element being present
int newlen = len - 1;
Object[] newElements = new Object[newlen];
for (int i = 0; i < newlen; ++i) {
if (eq(o, elements[i])) {
// found one; copy remaining and exit
for (int k = i + 1; k < len; ++k)
newElements[k-1] = elements[k];
setArray(newElements);
return true;
} else
newElements[i] = elements[i];
}
// special handling for last cell
if (eq(o, elements[newlen])) {
setArray(newElements);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
和add方法一样,此处也是使用的ReentrantLock来保证线程安全的,首先创建比当前数组长度小1的数组,然后循环判断如果相等就将数组后面元素前移,然后替换掉引用的对象。
public E get(int index) {
return (E)(getArray()[index]);
}
get方法就很直接了,没有使用同步关键字和锁,直接定位到数组指定位置。
最后我们来看看其迭代方法:
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
直接用存放数据的Object数组构造一个COWIterator对象,下面是主要代码:
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
整个设计非常的巧妙,非常适合于读多写少的应用场景。而且相比直接使用同步关键字或锁来说代价来得小,非常适用!