ArrayList 的类注释上,JDK 就提醒了我们,如果要把 ArrayList 作为共享变量的话,是线程不安全的,推荐我们自己加锁或者使用 Collections.synchronizedList 方法,其实 JDK 还提供了另外一种线程安全的 List,叫做 CopyOnWriteArrayList,这个 List 具有以下特征:

1.线程安全的,多线程环境下可以直接使用,无需加锁;

2.通过锁 + 数组拷贝 + volatile 关键字保证了线程安全;

3.每次数组操作,都会把数组拷贝一份出来,在新数组上进行操作,操作成功之后再赋值回去。

整体架构

从整体架构上来说,CopyOnWriteArrayList 数据结构和 ArrayList 是一致的,底层是个数组,只不过 CopyOnWriteArrayList 在对数组进行操作的时候,基本会分四步走:

  1. 加锁;
  2. 从原数组中拷贝出新数组;
  3. 在新数组上进行操作,并把新数组赋值给数组容器;
  4. 解锁。

除了加锁之外,CopyOnWriteArrayList 的底层数组还被 volatile 关键字修饰,意思是一旦数组被修改,其它线程立马能够感知到,代码如下:

1
private transient volatile Object[] array;

整体上来说,CopyOnWriteArrayList 就是利用锁 + 数组拷贝 + volatile 关键字保证了 List 的线程安全。

类注释

我们看看从CopyOnWriteArrayList的类注释上能得到哪些信息:

  1. 所有的操作都是线程安全的,因为操作都是在新拷贝数组上进行的;
  2. 数组的拷贝虽然有一定的成本,但往往比一般的替代方案效率高;
  3. 迭代过程中,不会影响到原来的数组,也不会抛出 ConcurrentModificationException 异常。

接着我们来看下 CopyOnWriteArrayList 的核心方法源码。

新增

新增有很多种情况,比如说:新增到数组尾部、新增到数组某一个索引位置、批量新增等等,操作的思路还是我们开头说的四步,我们拿新增到数组尾部的方法举例,来看看底层源码的实现: