-
概述:
CopyOnWrite简称为COW,是一种用于集合的并发访问优化策略。CopyOnWriteArrayList时通过数组的复制来实现线程安全性。在进行操作时,恢复至出一个新的数组并且在新的数组上进行修改,避免了原始数据的修改以此来保证线程安全。但是,因为每次都要对数组进行复制操作,带来一些过多的性能开销。除此之外,它的读操作是无锁的,所以CopyOnWriteArrayList适用于读多写少的场景。
-
基本思想:
当代码往一个集合容器中写入元素时(添加、修改、删除),并不是在原本的集合容器中写入数据,而是将当前的集合容器的内容复制到一个新的容器中,然后在新的容器中写入元素之后再将原容器的引用指向新的容器。
-
add()方法:
在添加元素时,先使用ReentrantLock关键字对关键代码块进行加锁,保证修改时不会出现线程竞争,再用Arrays.copyOf()方法创建出一个新的数组,长度比原来多1,将e添加到新数组的最后位置,使用setArray()方法将原来数组的指向设置为新数组,最后返回true表示添加成功。
源代码解析如下:
public boolean add(E e) {
//定义ReentrantLock锁
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
获得旧值数组elements
Object[] elements = getArray();
//获得旧值数组长度
int len = elements.length;
//使用Arrays.copyOf()复制出一个新的数组newElements
//长度+1
Object[] newElements = Arrays.copyOf(elements, len + 1);
//将e放入新数组的最后一个位置
newElements[len] = e;
//指向新的数组
setArray(newElements);
//返回true
return true;
} finally {
//使用finally关键字是无论上文有没有异常都会解锁
//解锁
lock.unlock();
}
}
-
remove()方法:
在移除元素时,使用ReentrantLock关键字对关键代码块进行加锁,保证修改时不会出现线程竞争。通过index()找到索引位置,索引为-1表示该列表中不存在该元素。否则使用remove()方法删除该索引处的元素。最后返回true表示删除成功。
源代码解析如下:
public E remove(int index) {
//定义ReentrantLock锁
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;
//为最后一位,将旧数组内容复制到len-1
if (numMoved == 0)
setArray(Arrays.copyOf(elements, len - 1));
else {
//否则创建新的数组并且长度—1
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();
}
}
-
set()方法:
通过get()方法获取到索引处的旧值oldvalue,如果oldvalue不等于新值element,则使用Arrays.copyOf()方法创建出一个新的数组element,长度和原数组一样,再将新值element存入数组elements中的索引处,使用setArray()方法将原来数组的指向设置为新数组,最后返回旧值oldvalue。
源代码解析如下:
public E set(int index, E element) {
//定义ReentrantLock锁
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//获得旧值数组elements
Object[] elements = getArray();
//根据索引回去旧值oldValue
E oldValue = get(elements, index);
//旧值不等于新值element
if (oldValue != element) {
//长度与旧值一致
int len = elements.length;
//使用Arrays.copyOf()复制出一个新的数组newElements
Object[] newElements = Arrays.copyOf(elements, len);
//新值存入新数组
newElements[index] = element;
//指向新的数组
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
//返回旧值
return oldValue;
} finally {
//解锁
lock.unlock();
}
}