我们知道,集合框架中大部分数据结构类都是单线程下的,不然HashMap、ArrayList等,今天来看一种线程安全的list,也就是CopyOnWriteArrayList。首先来看一下类图,如下:

从类图可以看出,CopyOnWriteArrayList实现了RandomAccess接口,可以支持快速访问。也实现了List、Serializable接口等。此外,在其内部定义了一个ReentrantLock作为独占锁。
1、构造函数
构造函数有三个,分别是无参构造函数,源码如下:

无参数构造函数,默认创建一个空数组。其次,有参数的构造函数如下:

此外,还有一种构造函数,可以传入collection。源码如下:

上面的代码就是将collection中的元素存储到本地的数组中。
2、add操作
add操作是向list中添加元素,其源码如下:

上面的源码中,首先获取锁。然后调用getArray得到本地的数组。然后调用Arrays的copyof方法将array数组复制到新数组中,接着添加元素到新数组中。最后,调用setArray方法将新数组newElements代替之前的array。最终,释放锁。
其他还有add(index,E)方法等,在某个位置插入元素。addAll方法,插入很多元素。这些原理都与add方法相似,不再叙述。
3、get操作
get操作是获取指定下标的元素,其源码如下:

其中,getArray方法返回本地数组。我们看看return调用的这个get方法的源码。

这个get方法就是返回指定下标的元素。这里在方法上面标注了unchecked,也就是说明index需要调用者自己检查是否越界。也就是说数组越界,会报IndexOutBoundsException。
4、set操作
set操作是设置指定下标的元素,也就是修改元素值,其源码如下:

从上面的源码可以看出,首先肯定还是加锁,最后是否锁的操作。这样可以阻止其他线程对array数组的修改。这个代码首先获取oldValue值,如果oldValue和传入的参数element不相等,才进行修改。修改的过程和add元素相似,都是新建一个数组,然后将array数组拷贝出来,然后修改指定下标值,最后重新将新数组设置成本地的array数组。
5、remove操作
remove操作是删除指定下标的元素,源码如下:

从源码可以看出,首先加锁、最后释放锁。在try语句中,首先获取当前array数组,然后调用get方法获取指定下标元素oldValue。然后计算新数组的长度numMoved。
如果numMoved为0,那么说明是删除最后一个元素,也就复制倒数第二个之前的元素即可。
否则,不是删除最后一个元素,那么就分成两段赋值到本地newElements数组中,并设置为新数组即可。
6、size操作

size操作是返回list中元素个数,由于底层依赖动态数组,所有这里直接返回数组的长度即可。
7、小结
CopyOnWriteArrayList是基于写时复制的方式进行线程安全的,也就是说写操作的时候,会获取锁,然后创建本地数组newElements,修改后将这个newElements设置为新的数组即可。
此外,CopyOnWriteArrayList中的迭代器是弱一致性的,也就是某个线程在迭代器输出的是当时获取的副本。其他线程如果该时刻修改了某个元素,迭代器那个线程不会输出最新的值的。这一点在使用上面需要注意
!




