Java并发包中并发List介绍
并发包中的List只有 CopyOnWriteArrayList 。 CopyOnWriteArrayList是一个线程安全的List,对其的修改操作都是在底层的一个复制的数组上进行的,也就是使用了 写时复制 策略。

在CopyOnWriteArrayList中,每个对象里有一个array数组对象用来存放具体元素,ReentrantLock独占锁对象用来保证同时只有一个线程能对array进行修改。
如果让我们自己做一个写时复制的线程安全的List我们会怎么做?我们需要考虑什么?
何时初始化list,初始化list元素为多少个,list是有限大小吗?
如何保证线程安全?
如何保证使用迭代器遍历list时的数据一致性。
主要方法解析
构造方法
1 | // 默认初始化个数0 |
增加方法
1 | public boolean add(E e) { |
获取指定位置的元素
1 | final Object[] getArray() { |
获取操作并没有加锁,可能会产生 弱一致性问题 。比如当线程A执行get,线程B执行删除操作。线程A这里可以分为两个步骤 1. 获取当前array 2. 通过获取到的array根据索引获得指定元素。
假如这个时候当线程A执行完步骤1,然后线程B执行删除操作,我们假设线程A需要获得的是index为1的元素,此时线程B将原来array复制到新的array中然后对这个新array进行remove(1)的操作,之后把这个新的array重新赋值到对象中的array中,当这一系列操作完成后,线程A获取指定元素,而这个时候线程A是通过原来array获取的,所以它仍能获取到index为1的元素。
例如对于以下程序 运行结果可能会是2或者3
1 | public class CopyOnWriteArrayListTest { |
修改指定元素
1 | public E set(int index, E element) { |
删除指定元素
1 | public E remove(int index) { |
弱一致性的迭代器
1 | static final class COWIterator<E> implements ListIterator<E> { |
在snapshot中保存着当前array的快照,也就是说迭代器所操作的是array的一个快照,所以当其他线程对array进行更改的时候,迭代器是感知不到的,这又会产生弱一致性问题。
我们来看一下实例
1 | public class CopyOnWriteArrayListTest { |

我们可以看到运行结果是1 2 3 4 5
我们来看一下创建iterator的源码
1 | public Iterator<E> iterator() { |
其实跟上面讲到的获取和删除的弱一致性一样,因为修改和删除操作时对一个快照进行修改和删除的,并且最终把新的快照地址重新赋值给array,虽然此时的array指向的地址已经变了,但是原来迭代器中获取array的地址并没有改变,所以迭代器操作的还是原来的array,这就是写时复制产生的弱一致性问题。