java的类上的注释说明:java.util.ArrayList的线程安全变体,其中所有可变操作(添加、设置等)都是通过制作底层数组的新副本来实现的。这通常成本太高,但当遍历操作的数量远远超过突变时,可能比其他方法更高效,当您不能或不想同步遍历,但需要排除并发线程之间的干扰时,这很有用。
通过ReentrantLock 保证操作数组时的线程安全,具体操作是复制数组
//保证线程安全的同步锁
final transient ReentrantLock lock = new ReentrantLock();
//底层存放数据的数组
private transient volatile Object[] array;
以下举几个常用的例子
public boolean add(E e) {
//获取锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//copy一个数组 数组的长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
//把元素放入新的数组中
newElements[len] = e;
//设置新的数组为当前数组
setArray(newElements);
return true;
} finally {
//释放锁
lock.unlock();
}
}
public E remove(int index) {
//获取锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//从数组中获取到指定的元素
E oldValue = get(elements, index);
//当前元素的后面元素的个数
int numMoved = len - index - 1;
//如果是0 则后面不需要向前移动 直接复制数组
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
//因为中间元素被移除掉,所以分两次拷贝 一份是被移除元素之前的元素和之后的元素
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
//设置新的数组为当前数组
setArray(newElements);
}
//返回被移除的元素
return oldValue;
} finally {
//释放锁
lock.unlock();
}
}
public E get(int index) {
return get(getArray(), index);
}
//获取元素是从当前数组中获取元素,没有加锁,
private E get(Object[] a, int index) {
return (E) a[index];
}
public int size() {
//返回当前数组的长度
return getArray().length;
}
从上述方法中可以看到CopyOnWriteArrayList是通过ReentrantLock 保证线程安全,对内部数组的操作是通过每次都是复制一个长度+1的新数组去存放元素。删除元素同理,对于读取数据并没有加锁。