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获取的仍然是旧数组的元素】

原文地址:https://www.cnblogs.com/jelly12345/p/14074206.html