1 CopyOnWriteArrayList的介绍
-
CopyOnWriteArrayList的本质是一个线程安全的ArrayList集合,
-
它适用于读多写少的场景,
-
它支持多个线程并发地读,但不支持多线程并发写,也就是说当一个线程在进行增、删、改的操作时,其他线程不能进行写操作,
-
它的实现原理是在读操作的方法不加锁,而在写操作加锁,每个写操作都是赋值一份原内容,在复制的数组上进行增删改操作,最后将修改后的数组引用指向原数组的引用,这样在一个线程没修改完之前,读操作都读取的是原数组的内容,不会读取脏数据
-
它的特点是,保证了数据的最终一致性,而不能保证数据的实时一致性,也就是说他最终获取的数据一定是正确的,但不鞥保证时刻获取的数据都是最新的数据
2 CopyOnWriteArrayList源码解读
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// 定义一个ReentrantLock类型的锁
final transient ReentrantLock lock = new ReentrantLock();
// 声明一个volatile修饰的Object类型的数组
private transient volatile Object[] array;
// array的getter和setter
final Object[] getArray() {
return array;
}
final void setArray(Object[] a) {
array = a;
}
// CopyOnWriteArrayList的无参构造方法,初始化一个容量为0的Object数组
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
// CopyOnWriteArrayList的有参构造方法,参数类型是Collection及其子类/实现类
public CopyOnWriteArrayList(Collection<? extends E> c) {
Object[] elements;
// 若初始化参数类型是CopyOnWriteArrayList
if (c.getClass() == CopyOnWriteArrayList.class)
// 获取参数的array数组对象,并存到elements局部对象中
elements = ((CopyOnWriteArrayList<?>)c).getArray();
else {
// 若初始化参数类型不是CopyOnWriteArrayList
// 将Collection类型的集合转换成数组
elements = c.toArray();
// 若elements不是Object[],将该elements数组复制到一个Object[]中,并传给elements
if (elements.getClass() != Object[].class)
elements = Arrays.copyOf(elements, elements.length, Object[].class);
}
// 调用set方法存值至array
setArray(elements);
}
// set方法:修改方法
public E set(int index, E element) {
// 降低作用域,得到reentrantLock锁
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
// 获取旧数组
Object[] elements = getArray();
// 获取即将被修改的旧值
E oldValue = get(elements, index);
// 若旧值与新值不等
if (oldValue != element) {
int len = elements.length;
// 复制旧数组至newElements
Object[] newElements = Arrays.copyOf(elements, len);
// 将index处的值覆盖为element
newElements[index] = element;
// 调用setter存值至array
setArray(newElements);
} else {
// 若修改的值和原值相等,直接调用setter存值至array,还是旧数组
setArray(elements);
}
// 返回修改前的值
return oldValue;
} finally {
// 释放锁
lock.unlock();
}
}
// 添加元素
public boolean add(E e) {
// ReentrantLock对象锁
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
// 获取旧数组
Object[] elements = getArray();
int len = elements.length;
// 复制一份并将容量加1
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 将元素添加至数组末尾
newElements[len] = e;
// 将新数组存入array
setArray(newElements);
return true;
} finally {
// 释放锁
lock.unlock();
}
}
// 在指定位置添加元素
public void add(int index, E element) {
// ReentrantLock锁对象
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
// 获取旧数组
Object[] elements = getArray();
int len = elements.length;
// 如果传入一个不合法的下标索引,则抛异常
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
// 声明一个新数组
Object[] newElements;
// 若修改的元素是最后一个元素,将原数组拷贝一份,并容量+1
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
// 若修改的元素不是最后一个元素,分段将不包含该位置的前段数组和后端数组复制到newElements数组
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
// 将修改的值,存到空出来的位置
newElements[index] = element;
// 调用setArray将新数组存到array数组
setArray(newElements);
} finally {
// 释放锁
lock.unlock();
}
}
// 删除操作
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;
// 判断是否删除的是数组最后一个元素
if (numMoved == 0)
// 如果是,复制除过最后一个元素外的所有元素,存到array中
setArray(Arrays.copyOf(elements, len - 1));
else {
// 若删除的不是最后一个元素,先创建一个比原来数组容量小1的数组
Object[] newElements = new Object[len - 1];
// 将原数组,除过删除元素下标位置,其他元素都复制到newElements
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
// 存
setArray(newElements);
}
// 返回删除前的元素
return oldValue;
} finally {
// 释放锁
lock.unlock();
}
}
// 删除连续范围内的值
void removeRange(int fromIndex, int toIndex) {
// 上ReentrantLock锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 如果传入的下标参数不合法,则抛出异常
if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
throw new IndexOutOfBoundsException();
int newlen = len - (toIndex - fromIndex);
int numMoved = len - toIndex;
// 判断删除范围是否为数组的后半段
if (numMoved == 0)
// 如果是,将前半段元素复制至elements数组
setArray(Arrays.copyOf(elements, newlen));
else {
// 若否,创建一个新数组
// 复制范围前半段的值至elements
Object[] newElements = new Object[newlen];
System.arraycopy(elements, 0, newElements, 0, fromIndex);
// 复制范围后半段的值至elements
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
// 将新数组传递给array
setArray(newElements);
}
} finally {
// 释放锁
lock.unlock();
}
}
// 保留该集合所有包含c的值
public boolean retainAll(Collection<?> c) {
// 如果传入参数为null,抛出空指针异常
if (c == null) throw new NullPointerException();
// 加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 获取旧数组
Object[] elements = getArray();
// 获取旧数组的长度
int len = elements.length;
// 若原数组存在元素
if (len != 0) {
// 定义新数组长度为0
int newlen = 0;
// 定义一个容量为len的临时数组
Object[] temp = new Object[len];
// 遍历旧数组
for (int i = 0; i < len; ++i) {
Object element = elements[i];
// 如果在c集合中包含该元素,将该元素存入临时数组
if (c.contains(element))
temp[newlen++] = element;
}
// 若符合条件元素数组temp的容量不为原数组的大小,则将容量小的数组复制至原先定义的容量大的temp数组中,目的去除空元素
if (newlen != len) {
setArray(Arrays.copyOf(temp, newlen));
return true;
}
}
return false;
} finally {
// 释放锁
lock.unlock();
}
}
// 清空元素
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 将一个空数组设置给array
setArray(new Object[0]);
} finally {
lock.unlock();
}
}
// 将给定集合存入指定位置
public boolean addAll(int index, Collection<? extends E> c) {
// 将集合转换成数组
Object[] cs = c.toArray();
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 传入不合法的index,抛出下标越界异常
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
// 如果传入集合大小为0,则返回false
if (cs.length == 0)
return false;
int numMoved = len - index;
Object[] newElements;
// 若指定下标位置为数组末尾,则直接将给定数组添加值末尾,如果第二个参数的长度大于原数组的长度,则填充默认值
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + cs.length);
else {
// 若没在末尾添加数组,先创建一个长度为len + cs.length的数组
newElements = new Object[len + cs.length];
//分别将指定下标位置之前的数组复制至新数组,将剩余数组元素复制至新数组
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index,
newElements, index + cs.length,
numMoved);
}
// 将目标集合复制到指定位置
System.arraycopy(cs, 0, newElements, index, cs.length);
// 存
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
}