CopyOnWriteArrayList是JUC中比较简单的一个类,可以理解为线程安全的ArrayList,所以假如理解了ArrayList的不安全性,也就理解了CopyOnWriteArrayList。
为什么说ArrayList是线程不安全的?
ArrayList底层使用的数据结构是数组,数组有容量和下标;比如说现在数组的容量是10,里面已经添加了9个元素;此时A和B两个线程同时向数组中添加元素,A线程判断数组中还有几个元素,发现还有一个位置,于是准备添加元素,而B线程也发现还有一个位置,也准备添加元素,那么最后的结果要么是数组越界,要么AB两个线程元素互相覆盖。
参考代码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 确认是否有位置
elementData[size++] = e; // 添加元素
return true;
}
CopyOnWriteArrayList:增
// 假如没有指定下标 则添加到数组最后一个位置
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock(); // 由于加锁过程中有可能出错,所以一定要放到try外
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();
}
}
// 在确定索引添加元素
public void add(int index, E element) {
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;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index); // 数组移位
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
// 在末尾添加一批元素
public boolean addAll(Collection<? extends E> c) {}
// 从指定位置开始添加一批元素
public boolean addAll(int index, Collection<? extends E> c) {}
// 如果不存在就添加
public boolean addIfAbsent(E e) {}
CopyOnWriteArrayList:删
// 删除指定位置
public E remove(int index) {}
// 删除指定元素
public boolean remove(Object o) {}
// 删除一批元素
public boolean removeAll(Collection<?> c) {}
CopyOnWriteArrayList:改
/**
1、同一时间只能有一个线程执行修改操作;
2、为了不影响查,把原来的数组复制一份出来,在新的数组上修改;
**/
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 {
setArray(elements); // 根据happens-before原则,这里应该volatile写一下
}
return oldValue; // 一旦修改成功旧值就找不到了,所以这里返回旧值,以防后面还需要使用旧值----好想法,具有借鉴意义
} finally {
lock.unlock();
}
}
CopyOnWriteArrayList:查
优点:
1、读不加锁,比Vector,SynchronizedList性能高;
2、以空间换时间,提升性能;
缺点:
1、写操作多,会消耗很多内存;
2、读延迟;
适用场景:读多写少
网友评论