juc 下的集合之六 (CopyOnWriteArrayList )
一、基本思想
?? ? ? ArrayList的一个变体,通过对原来数组的拷贝,来保证不同操作情况下的线程安全。
?? ?? 这个容器类内部有大量的数组拷贝操作
二、源码解析
???? 2.1 基本数据结构
/** The lock protecting all mutators */ transient final ReentrantLock lock = new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private volatile transient Object[] array;
?? 很简单,里面一个数组而已,声明成volatile保证一个线程的修改对其它线程立即可见
?
? ? 2.2 get操作
public E get(int index) { return (E)(getArray()[index]); //数组的简单遍历操作,没有锁,没有拷贝 } final Object[] getArray() { return array; }public Object[] toArray()//返回的是一个数组副本 Object[] elements = getArray();return Arrays.copyOf(elements, elements.length); }
?
?
??? 2.3 add操作
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();//释放锁放在final中} }
?2.4 remove 操作
public E remove(int index) {final ReentrantLock lock = this.lock;lock.lock();try { Object[] elements = getArray(); int len = elements.length; Object oldValue = 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 (E)oldValue;} finally { lock.unlock();} }
?很简单,和add操作基本一样
?
三、适用范围
?????? 官方解释,来自JDK源码中的注释。
?????? 尽管数组的大量拷贝的代价很大,但是在某些情况下可能会更加有效。比如说你需要用多个线程对数组大量的遍历,而你又不想同步你的遍历的时候。
因为你每次遍历其实是你当前数组的一个快照,所以你的程序永远不会出现"ConcurrentModifacationException"。
?????? 内存一致性问题:一个线程在处理这个数组的时候都是处理这个数组的副本,因此线程在处理过程中的这段时间,无法获得其它的线程对此数组的更改。
?
四、测试
?
//TODO :)