一 CopyOnWrite容器概述

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayListCopyOnWriteArraySet

 

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

 

二 CopyOnWriteArrayList的实现原理及引申

核心就是读时候不加锁,写时候进行同步,copy容器 -> 引用变更,copyOnWriteArrayList用到底层native的System.arraycopy方法。

下面仿照源码写的MyCopyOnWriteArrayList(看起来????的setArray是为了gc)

「java.util.concurrent并发包」之 CopyOnWrite
  1 public class MyCopyOnWriteArrayList<E> {
  2 
  3     private transient final ReentrantLock reentrantLock = new ReentrantLock();
  4 
  5     private volatile transient Object[] array;
  6 
  7     public Object[] getArray() {
  8         return array;
  9     }
 10 
 11     public void setArray(Object[] array) {
 12         this.array = array;
 13     }
 14 
 15     public MyCopyOnWriteArrayList() {
 16         setArray(new Object[0]);
 17     }
 18 
 19     public MyCopyOnWriteArrayList(Collection<? extends E> collection) {
 20         Object[] elements = collection.toArray();
 21         if (elements.getClass() != Object[].class) {
 22             elements = Arrays.copyOf(elements, elements.length, Object[].class);
 23         }
 24         setArray(elements);
 25     }
 26 
 27     public MyCopyOnWriteArrayList(E[] toCopyIn) {
 28         setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
 29     }
 30 
 31     public int size() {
 32         return getArray().length;
 33     }
 34 
 35     public boolean isEmpty() {
 36         return size() == 0;
 37     }
 38 
 39     /**
 40      * 返回Object类型数组
 41      */
 42     public Object[] toArray() {
 43         Object[] elements = getArray();
 44         return Arrays.copyOf(elements, elements.length);
 45     }
 46 
 47     /**
 48      * 返回传入的T类型数组
 49      */
 50     @SuppressWarnings("unchecked")
 51     public <T> T[] toArray(T a[]) {
 52         Object[] elements = getArray();
 53         int len = size();
 54         if (a.length < len) {
 55             return (T[]) Arrays.copyOf(elements, len, a.getClass());
 56         } else {
 57             System.arraycopy(elements, 0, a, 0, len);
 58             return a;
 59         }
 60     }
 61 
 62     @SuppressWarnings("unchecked")
 63     private E get(Object[] a, int index) {
 64         return (E) a[index];
 65     }
 66 
 67     public E get(int index) {
 68         return get(getArray(), index);
 69     }
 70 
 71     public E set(int index, E element) {
 72         final ReentrantLock reentrantLock = this.reentrantLock;
 73         reentrantLock.lock();
 74         try {
 75             Object[] elements = getArray();
 76             E oldValue = get(elements, index);
 77 
 78             if (oldValue != element) {
 79                 int len = elements.length;
 80                 Object[] newElements = Arrays.copyOf(elements, len);
 81                 newElements[index] = element;
 82                 setArray(newElements);
 83             } else {
 84                 // 意义何在,没太看懂
 85                 setArray(elements);
 86             }
 87             return oldValue;
 88         } finally {
 89             reentrantLock.unlock();
 90         }
 91     }
 92 
 93     public boolean add(E e) {
 94         final ReentrantLock reentrantLock = this.reentrantLock;
 95         reentrantLock.lock();
 96         try {
 97             Object[] elements = getArray();
 98             int len = size();
 99             Object[] newElements = Arrays.copyOf(elements, len + 1);
100             newElements[len] = e;
101             setArray(newElements);
102             return true;
103         } finally {
104             reentrantLock.unlock();
105         }
106     }
107 
108     public E remove(int index) {
109         final ReentrantLock reentrantLock = this.reentrantLock;
110         reentrantLock.lock();
111         try {
112             Object[] elements = getArray();
113             int len = elements.length;
114             E oldValue = get(elements, index);
115             int numMoved = len - index - 1;
116             if (numMoved == 0)
117                 setArray(Arrays.copyOf(elements, len - 1));
118             else {
119                 Object[] newElements = new Object[len - 1];
120                 System.arraycopy(elements, 0, newElements, 0, index);
121                 System.arraycopy(elements, index + 1, newElements, index,
122                         numMoved);
123                 setArray(newElements);
124             }
125             return oldValue;
126         } finally {
127             reentrantLock.unlock();
128         }
129     }
130 }
MyCopyOnWriteArrayList

相关文章: