0 前言
我们知道 ArrayList 非线程安全,需要自己加锁或者使用 Collections.synchronizedList
包装.
从JDK1.5开始JUC里提供了使用 CopyOnWrite 机制实现的并发容器线程安全的 List - CopyOnWriteArrayList,简称 COW
1 CopyOnWrite 设计思想
1.1 基本概念
CopyOnWrite 写时复制.
一般来说就是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器复制出一个新的容器,往新的容器里添加元素,添加完元素之后,再将原容器引用指向新容器.
即一开始大家都在共享同一内容,当有人想修改该内容时,才会真地把内容copy出去形成一个新的内容然后再改,这是一种延时懒惰策略.
1.2 设计优点
可并发读 CopyOnWrite 容器,而无需加锁,因为当前容器不会添加任何元素.
所以这也是一种读写分离的思想,读写的是不同的容器.
2 继承体系
- 和 ArrayList 的继承体系类似
3 属性
- 保护所有更改器的锁
- 仅能通过getArray / setArray访问的数组
- lock 内存偏移量
4 构造方法
4.1 无参
- 创建一个空 list
4.2 有参
- 创建一个列表,该列表包含指定集合的元素,其顺序由集合的迭代器返回。
- 创建一个保存给定数组副本的列表
- 下面开始看源码,到底是如何实现写时复制的.
5 add(E e)
向 COW 里添加元素,是需要加锁的,否则并发写时 copy 出N个副本!
getArray
- 获取数组.非priavte,以便也可以从CopyOnWriteArraySet类(直接组合了CopyOnWriteArrayList作为成员变量)访问
setArray
- 将引用设置到新数组
都加锁,为什么还需要拷贝数组,而不直接在原数组修改?
- volatile 修饰的是数组引用!简单的在原来数组修改几个元素的值,这种操作是无法发挥可见性的,必须通过修改数组内存地址
- 在新数组上执行 copyOf,对原数组无任何影响,只有新数组完全拷贝完成之后,外部才能访问,避免了原数组数据变动可能造成的不良影响