CopyOnWrite是一种用于集合的并发访问的优化策略。
基本思想是:
允许多线程并发读取,只有在写入时(添加、修改、删除)的时候,才会从原来的数据复制一个副本出来,然后修改这个副本,最后把原数据替换成当前的副本。写入的同时,查询操作不会被阻塞,而是继续读取旧的数据。
这样做的好处是实现对CopyOnWrite集合容器容器写入时线程安全,但同时不会影响并发的读取操作。它是一种读写分离的思想。
CopyOnWriteArrayList相当于线程安全的ArrayList内部存储结构采用Object数组,采用ReentrantLock锁实现线程安全。
在Java的并发包里有两种集合容器使用了CopyOnWrite并发机制,分别是CopyOnWriteArrayList和CopyOnWriteArraySet.
下面让我们先来看一看CopyOnWriteArrayList的源码
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//创建一个可重入锁
final transient ReentrantLock lock = new ReentrantLock();
//创建一个Object类型的数组 transient让定义的变量不被序列化
private transient volatile Object[] array;
//获取原来数组
final Object[] getArray() {
return array;
}
//将元素写入数组
final void setArray(Object[] a) {
array = a;
}
//无参构造方法创建一个长度为0的新数组
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
//读取数据,允许多个线程并发读取数据
@SuppressWarnings("unchecked")
//传入数组和指定下标,返回指定下标的元素
private E get(Object[] a, int index) {
return (E) a[index];
}
//根据指定下标调用get()方法从原数组中读取元素
//返回数组和下标
public E get(int index) {
return get(getArray(), index);
}
//修改数组中的元素 传入修改位置和元素
public E set(int index, E element) {
//创建锁
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//获取原数组
Object[] elements = getArray();
//通过get()方法获取原数组在传入的下标位置的元素
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();
}
}
//添加元素到集合时,会对线程加ReentrantLock锁保证线程安全。
public boolean add(E e) {
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;
//将原数组的引用指向新数组
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;
//如果索引的范围小于0或者大于数组长度 抛异常
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
//需要移动的元素数量
int numMoved = len - index;
//需要移动的元素数量为0 表示在末尾添加元素
if (numMoved == 0)
//创建新数组将原数组中元素复制进去
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
//将原数组中0到索引位置的元素复制进新数组
System.arraycopy(elements, 0, newElements, 0, index);
//将索引+1位置之后的元素全部复制进新数组
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
//在索引位置插入元素
newElements[index] = element;
//将原数组的引用指向新数组
setArray(newElements);
} finally {
//释放锁
lock.unlock();
}
}
//删除指定位置元素
public E remove(int index) {
//创建锁
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)
setArray(Arrays.copyOf(elements, len - 1));
else {
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();
}
}
//删除指定范围的元素
void removeRange(int fromIndex, int toIndex) {
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)
setArray(Arrays.copyOf(elements, newlen));
else {
//创建新数组
Object[] newElements = new Object[newlen];
//将剩余元素复制进新数组
System.arraycopy(elements, 0, newElements, 0, fromIndex);
System.arraycopy(elements, toIndex, newElements,
fromIndex, numMoved);
setArray(newElements);
}
} finally {
lock.unlock();
}
}
//求交集
public boolean removeAll(Collection<?> c) {
//集合为空 抛异常
if (c == null) throw new NullPointerException();
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
//获取原数组长度
int len = elements.length;
//如果原数组不为空遍历原数组判断集合中是否包含原数组中的元素,如果不包含,就将元素存入新数组
if (len != 0) {
int newlen = 0;
Object[] temp = new Object[len];
for (int i = 0; i < len; ++i) {
Object element = elements[i];
if (!c.contains(element))
temp[newlen++] = element;
}
//新数组和原数组长度不一样,将原数组的引用指向新数组
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 {
setArray(new Object[0]);
} finally {
//释放锁
lock.unlock();
}
}
//向集合中以集合为单位添加元素
public boolean addAll(Collection<? extends E> c) {
//判断传入的集合类型和当前类型是否相同(以数组为单位存储)
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
//获取到的数组为空
if (cs.length == 0)
return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (len == 0 && cs.getClass() == Object[].class)
setArray(cs);
else {
//复制原数组内容到新数组
Object[] newElements = Arrays.copyOf(elements, len + cs.length);
System.arraycopy(cs, 0, newElements, len, cs.length);
setArray(newElements);
}
return true;
} finally {
lock.unlock();
}
}
//排序
public void sort(Comparator<? super E> c) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取原数组,并将原数组复制进新数组
Object[] elements = getArray();
Object[] newElements = Arrays.copyOf(elements, elements.length);
@SuppressWarnings("unchecked") E[] es = (E[])newElements;
//数组排序
Arrays.sort(es, c);
setArray(newElements);
} finally {
lock.unlock();
}
}
//判断两个数组是否相等
public boolean equals(Object o) {
//如果传入的与要比较的内存地址相同,那么他们相同
if (o == this)
return true;
//判断是否为List接口类型
if (!(o instanceof List))
return false;
//下转型
List<?> list = (List<?>)(o);
//迭代器遍历数组
Iterator<?> it = list.iterator();
Object[] elements = getArray();
int len = elements.length;
for (int i = 0; i < len; ++i)
//如果长度不一样,则数组不同
if (!it.hasNext() || !eq(elements[i], it.next()))
return false;
if (it.hasNext())
return false;
return true;
}
//计算哈希值
public int hashCode() {
int hashCode = 1;
//获取原数组
Object[] elements = getArray();
//获取原数组长度
int len = elements.length;
//遍历数组
for (int i = 0; i < len; ++i) {
Object obj = elements[i];
//计算哈希值
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
return hashCode;
}
}