1、CopyOnWrite容器有两种: ·CopyOnWriteArrayList ·CopyOnWriteArraySet CopyOnWrite容器简称COW容器,其特点如下: 1)CopyOnWrite容器即写时复制的容器。 2)通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器,读时都是访问旧容器,写时才需要加锁。 3)这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。 2、CopyOnWriteArrayList类继承结构: public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable;
3、对于CopyOnWriteArrayList主要查看以下几个方法: ·创建:CopyOnWriteArrayList() ·添加元素:即add(E)方法 ·获取单个对象:即get(int)方法 ·删除对象:即remove(E)方法 ·遍历所有对象:即iterator(),在实际中更常用的是增强型的for循环去做遍历。
4、构造方法CopyOnWriteArrayList,其相关代码: /** 只能 getArray/setArray访问数组. */ private volatile transient Object[] array;
/**
* 获取数组
*/
final Object[] getArray() {
return array;
}
/**
* 设置数组
*/
final void setArray(Object[] a) {
array = a;
}
/**
*创建一个空的数组,Object[0],而ArrayList则是10
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
5、添加元素 public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock(); //获取全局锁(独占锁),获取不到则阻塞
try {
Object[] elements = getArray(); //获取当前的数组
int len = elements.length;
/*
* Arrays.copyOf(elements, len + 1)的大致执行流程:
* 1)创建新数组,容量为len+1,
* 2)将旧数组elements拷贝到新数组,
* 3)返回新数组
*/
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e; //新数组的末尾元素设成e
setArray(newElements); //将旧数组指向新数组引用
return true;
} finally {
lock.unlock();
}
}
6、获取指定元素
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc} //找不到指定的元素则抛出异常
*/
public E get(int index) {
return get(getArray(), index); //先获取数组,在读取指定位置的元素。
}
从以上代码可以看出,读取元素时不加锁的,此时采用的是弱一致性策略。获取指定位置的元素分为两步,首先获取到当前list里面的array数组,这里称为步骤1,然后通过随机访问的下标方式访问指定位置的元素,这里称为步骤2。
因为整个过程并没有加锁,这就可能会导致当执行完步骤1后执行步骤2前,另外一个线程C进行了修改操作,比如remove操作,就会进行写时拷贝删除当前get方法要访问的元素,并且修改当前list的array为新数组。而这之后步骤2 可能才开始执行,步骤2操作的是线程C删除元素前的一个快照数组(因为步骤1让array指向的是原来的数组),所以虽然线程C已经删除了index处的元素,但是步骤2还是返回index处的元素,这其实就是写时拷贝策略带来弱一致性。
7、修改指定元素,若元素不存在则抛出ndexOutOfBoundsException。 /** * Replaces the element at the specified position in this list with the * specified element. * * @throws IndexOutOfBoundsException {@inheritDoc} */ 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();
}
}
如果指定位置元素与新值一样,则为了保障volatile语义,还是需要重新设置下array,虽然array内容并没有改变(为了保证 volatile 语义是考虑到 set 方法本身应该提供 volatile 的语义).。
7、删除元素
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];
//分两次拷贝删除后的数组
//先复制index位置之前的元素,
System.arraycopy(elements, 0, newElements, 0, index); //index在此处表示要复制的长度或元素个数
//复制inex位置之后的元素
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
//其中:src表示源数组,srcPos表示源数组要复制的起始位置,desc表示目标数组,length表示要复制的长度。
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock(); //操作完成,解除锁定
}
}
8、弱一致性的迭代器
/**
* Returns an iterator over the elements in this list in proper sequence. 返回一定顺序的的元素列表(迭代器)
*
* <p>The returned iterator provides(提供) a snapshot(快照) of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
* <tt>remove</tt> method. 大概意思就是:返回的迭代器是list列表的一个快照,在整个迭代的过程中不需要同步(加锁),这个迭代器不支持remove操作。
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
/**
* {@inheritDoc}
*
* <p>The returned iterator provides a snapshot of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
* <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods. 不支持remove/set/add等操作
*/
public ListIterator<E> listIterator() {
return new COWIterator<E>(getArray(), 0);
}
/**
* {@inheritDoc}
*
* <p>The returned iterator provides a snapshot of the state of the list
* when the iterator was constructed. No synchronization is needed while
* traversing the iterator. The iterator does <em>NOT</em> support the
* <tt>remove</tt>, <tt>set</tt> or <tt>add</tt> methods.
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public ListIterator<E> listIterator(final int index) {
Object[] elements = getArray();
int len = elements.length;
if (index<0 || index>len)
throw new IndexOutOfBoundsException("Index: "+index);
return new COWIterator<E>(elements, index);
}
private static class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */array数组的一个快照
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */将由后续调用next返回的元素的索引。即数组下标或索引
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
//判断是否有下个元素
public boolean hasNext() {
return cursor < snapshot.length;
}
......
//获取当前元素,索引加1
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
.....
}
这里为什么说snapshot是list的快照呢?明明是指针传递的引用,而不是拷贝。如果在该线程使用返回的迭代器遍历元素的过程中,其他线程没有对list进行增删改,那么snapshot本身就是list的array,因为它们是引用关系。
但是如果遍历期间,有其他线程对该list进行了增删改,那么snapshot就是快照了,因为增删改后list里面的数组被新数组替换了,这时候老数组只有被snapshot所引用,所以这也就说明获取迭代器后,使用改迭代器进行遍历元素时候,其它线程对该list进行的增删改是不可见的, 因为它们操作的是两个不同的数组,这也就是弱一致性的达成。