CopyOnWriteArrayList总结
一:使用场景
CopyOnWriteArrayList适用于场景为读多写少的集合应用场景中。
二:CopyOnWrite容器
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
三:CopyOnWriteArrayList的实现原理
源码如下所示:
1 public boolean add(E e) {
2 final ReentrantLock lock = this.lock;
3 lock.lock();
4 try {
5 Object[] elements = getArray();
6 int len = elements.length;
7 Object[] newElements = Arrays.copyOf(elements, len + 1);
8 newElements[len] = e;
9 setArray(newElements);
10 return true;
11 } finally {
12 lock.unlock();
13 }
14 }
15
16 public E remove(int index) {
17 final ReentrantLock lock = this.lock;
18 lock.lock();
19 try {
20 Object[] elements = getArray();
21 int len = elements.length;
22 E oldValue = get(elements, index);
23 int numMoved = len - index - 1;
24 if (numMoved == 0)
25 setArray(Arrays.copyOf(elements, len - 1));
26 else {
27 Object[] newElements = new Object[len - 1];
28 System.arraycopy(elements, 0, newElements, 0, index);
29 System.arraycopy(elements, index + 1, newElements, index,
30 numMoved);
31 setArray(newElements);
32 }
33 return oldValue;
34 } finally {
35 lock.unlock();
36 }
37 }
38
39 public E set(int index, E element) {
40 final ReentrantLock lock = this.lock;
41 lock.lock();
42 try {
43 Object[] elements = getArray();
44 E oldValue = get(elements, index);
45
46 if (oldValue != element) {
47 int len = elements.length;
48 Object[] newElements = Arrays.copyOf(elements, len);
49 newElements[index] = element;
50 setArray(newElements);
51 } else {
52 // Not quite a no-op; ensures volatile write semantics
53 setArray(elements);
54 }
55 return oldValue;
56 } finally {
57 lock.unlock();
58 }
59 }
该集合对写操作实现了加锁操作,对读操作没有实现加锁,读操作源码如下所示:
1 private E get(Object[] a, int index) { 2 return (E) a[index]; 3 }
1 /** The array, accessed only via getArray/setArray. */ 2 private transient volatile Object[] array;
其实经过源码分析之后,我们发现把元素存储到一个全局的数组里面,该数据是使用volatile关键字进行修饰的,这样其实我们已经理解了其中的原理,就是使用volatile的可见性。
四:CopyOnWriteArrayList的优缺点
CopyOnWrite容器有很多优点,但是同时也存在两个问题,即内存占用问题和数据一致性问题。所以在开发的时候需要注意一下。
①:内存占用问题。因为CopyOnWrite的写时复制机制,所以在进行写操作的时候,内存里会同时驻扎两个对象的内存,旧的对象和新写入的对象(注意:在复制的时候只是复制容器里的引用,只是在写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存)。
②:数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。因为复制和操作元素需要一点儿时间,所以会有延迟,所以如果你希望写入的数据,马上能读到,要求数据强一致性的话,请不要使用CopyOnWrite容器。【当执行add或remove操作没完成时,get获取的仍然是旧数组的元素】