今天来说一说CopyOnWriteArrayList类,纵观JUC包下并发List只有CopyOnWriteArrayList类。CopyOnWriteArrayList是一个线程安全的ArrayList,从它的命名也能看出在进行写操作的时候会进行复制,这种写时复制策略会产生弱一致性问题。下面对CopyOnWriteArrayList类继续剖析。
在CopyOnWriteArrayList类中都是围绕着以下两个成员变量进行操作的:
final transient ReentrantLock lock =new ReentrantLock();
private transient volatile Object[] array;
lock独占锁对象,保证同时只能有一个线程对array数据进行写操作。关于ReentrantLock独占锁的结束点这里。
array数组用来存放具体的元素,用关键字volatile修饰保证了内存的可见性。
下面对CopyOnWriteArrayList常用方法进行源码剖析。
add(E var1)
首当其冲的当然是添加操作,CopyOnWriteArrayList的add方法使用ReentrantLock保证同一时间只有一个线程对array进行操作,在进行添加元素会先创建一个新数组,拷贝array数组中的所有元素,并将新增元素添加到新数组之后再复制给array数组。由于每次新增都会创建一个新数组并且长度是原数组长度加1,所有CopyOnWriteArrayList是无界的。
public boolean add(E var1) {
//(1)
ReentrantLock var2 =this.lock;
var2.lock();
boolean var6;
try {
Object[] var3 =this.getArray();
int var4 = var3.length;
//(2)
Object[] var5 = Arrays.copyOf(var3, var4 +1);
var5[var4] = var1;
//(3)
this.setArray(var5);
var6 =true;
}finally {
//(4)
var2.unlock();
}
return var6;
}
(1)获取独占锁对象,保证只有一个线程进行array数组的写操作。
(2)创建一个新数组,长度是array数组长度加1,并将array数组中的元素拷贝到新数组中。将新数组的最后一个元素赋值为需要增加的元素。
(3)将新数组赋值个array数组。
(4)释放独占锁对象,注意:在开发使用中也需要向上述源码一样在finally中进行锁的释放,确保即使发生异常也对锁进行释放。
remove(int var1)
由于remove(Object var1) 、remove(Object var1, Object[] var2, int var3)方法原理都是一样的,所以下面就对remove(int var1)源码进行剖析。
删除array数组中指定下标的元素,首先获取独占锁,保证同一时间只有一个线程对array数据进行写操作,然后获取数组中要被删除的元素,并把剩余的元素复制到一个新数组,将新数组赋值为array数组,最后释放锁。
public E remove(int var1) {
//(1)
ReentrantLock var2 =this.lock;
var2.lock();
Object var11;
try {
Object[] var3 =this.getArray();
int var4 = var3.length;
//(2)
Object var5 =this.get(var3, var1);
int var6 = var4 - var1 -1;
//(3)
if (var6 ==0) {
this.setArray(Arrays.copyOf(var3, var4 -1));
}else {
Object[] var7 =new Object[var4 -1];
System.arraycopy(var3, 0, var7, 0, var1);
System.arraycopy(var3, var1 +1, var7, var1, var6);
this.setArray(var7);
}
var11 = var5;
}finally {
//(4)
var2.unlock();
}
return var11;
}
(1)获取独占锁,保证只有一个线程进行写操作。
(2)获取指定下标的元素。
(3)判断要删除的是否为最后一个元素,把除需要删除的元素之外的元素拷贝到一个新数组,并将新数组赋值给array数组。
(4)最终释放独占锁。
get(int var1)
获取指定下标的元素,该方法并没有进行加锁同步,所以在获取元素的时候可能该元素已经被其他线程删除了,这就是CopyOnWriteArrayList类中写时复制策略产生的弱一致性问题。
public E get(int var1) {
return this.get(this.getArray(), var1);
}
final Object[] getArray() {
return this.array;
}
private E get(Object[] var1, int var2) {
return var1[var2];
}
从上面的源码介绍中可以知道,对CopyOnWriteArrayList类的写操作都会创建一个新的数组并对原数组的元素进行拷贝,当在写频繁并元素多的情况下CopyOnWriteArrayList的性能可想而知,并且还有一致性的问题,所以在实现开发中并不会怎么用到CopyOnWriteArrayList类。
今天的分享就到这,有看不明白的地方一定是我写的不够清楚,所有欢迎提任何问题以及改善方法。