一 CopyOnWrite容器概述
Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
二 CopyOnWriteArrayList的实现原理及引申
核心就是读时候不加锁,写时候进行同步,copy容器 -> 引用变更,copyOnWriteArrayList用到底层native的System.arraycopy方法。
下面仿照源码写的MyCopyOnWriteArrayList(看起来????的setArray是为了gc)
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 }