CopyOnWriteArrayList 入门及介绍
本文详细介绍CopyOnWriteArrayList,从概述、原理、使用、源码、缺点等方面进行说明
背景
简要介绍下CopyOnWriteArrayList产生的背景
1.Vedtor 和SynchronizedList的锁力度比较大,基本上可以认为都是加锁在方法层面,并发度降低。(只有一把锁)
2.CopyOnWriteArrayList降低了锁的力度,并且在迭代时是可以编辑的。
3.CopyOnWrite容器中其他的实现,如:CopyOnWriteArraySet
适用场景
1.读操作需要足够快,写操作慢一点没啥关系;比如系统中黑名单、监听器(监听很多的时间,读的场景多)
读写规则
我们都知道读写锁的工作原理,即为多读一写。
为了将读的性能提高到最大,CopyOnWrite不再加读锁,只提供写写的互斥操作。
实例代码
首先我们演示ArrayList在迭代的时候进行了新增或删除,这里我们给出结论:ArrayList在迭代的时候不允许修改,见下代码
package com.yang.concurrent;
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListIteratorDemo {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Iterator<Integer> iterator=list.iterator();
while (iterator.hasNext()){
Integer item=iterator.next();
System.out.println(item);
if (item==2){
list.remove(2);
}
}
}
}
运行结果如下:不允许对迭代过程中对List进行操作。

我们用CopyOnWriteArrayList在迭代的时候进行下删除获取新增。
package com.yang.concurrent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListIteratorDemo {
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer item = iterator.next();
System.out.println(item);
if (item == 2) {
list.remove(2);
}
System.out.println(list);
}
}
}
运行结果如下,允许在迭代的过程中进行对List操作,不影响迭代。如下图。

原理
我们看下CopyOnWrite的原理。我们用的时候,重新拷贝一份新的,用完之后,将指针指向原来的地址。
创建新的副本,读写分离
以下代码演示迭代过程中数据过期的问题,见下代码。
package com.yang.concurrent;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
/**
*本实例演示迭代过程中数据过期的问题
*/
public class CopyOnWriteArrayListDemo2 {
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>(new Integer[]{1, 2, 3});
//迭代的数据取决了迭代器的生成时间
Iterator<Integer> iterator1 = list.iterator();
list.add(4);
Iterator<Integer> iterator2 = list.iterator();
iterator1.forEachRemaining(System.out::println);
iterator2.forEachRemaining(System.out::println);
}
}
CopyOnWriteArrayList缺点
1.不能保证实时数据一致性
2.内存占用问题,写操作时复制的,会有内存开销
源码分析
我们从初始化、锁、写操作、读操作进行分析。
初始化代码如下所示:

观察下使用的Lock锁。
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
.........
读操作的实现代码如下,我们发现未加锁,则读操作可能获取的数据不是最新的。
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
我们重点分析下写操作,我们发现写的时候,是互斥的,需要获取ReentrantLock,对原来的数组进行拷贝,待写入完成后,重新改变引用地址。如下所示:
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 {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}

浙公网安备 33010602011771号