CopyOnWriteArrayList核心源码解读

226 阅读2分钟

前言

以后简称为CWList,它为什么被称为线程安全的数组的呢?

中文名又叫写时复制数组,那就从写开始探索一下它的奥秘把。

结构

image.png

从它的结构图可以看到,所有的逻辑都是它自身实现的,没有继承任何类和抽象类。

构造方法

image.png

从它的3个构造方法可以看到,CWList是不能指定初始大小长度的。要么就是0长度,要么就传入新数组进来替代容器。

元素容器

image.png

可以看到CWList只有2个属性,一个用来加锁的lock,一个用来存放数据的容器 array;

并且array添加了volatile属性,这个属性的作用就是保证各线程对于该属性的可见性以及有序性。

简而言之,只要打上了volatile关键的属性,它的修改就能立即被其他线程'看到'。

add方法

image.png

add方法整个过程很简单清晰:

  1. 加上一把锁,防止其他线程同时操作
  2. 复制一份原数组长度+1的新数组出来
  3. 在新数组上进行增加元素
  4. 设置新数组为新的容器array
  5. 解锁

到这应该就明白为什么它被称为写时复制数组了,因为它的就是在写的时候复制一份新数组出来操作,从而不影响其他数组的读取,极大的提高了并发能力。

这个又和MySQL的mvcc很像,读取的都是快照数据。

再来看一下另一个add方法又是如何实现的

image.png

这个方法就是在指定位置插入元素,我们可以先自己分析一下,如果我们要实现这个方法需要考虑什么?

  • 第一个考虑的肯定是位置范围如何定?
  • 第二个考虑的是在中间插入怎么处理?

这里CWList给了我们答案;

  • 第一个是只能在本身长度 - 本身长度+1的范围内进行新增
  • 第二个是中间插入就插入中间位置,后面的元素往后移位。

分析一下该方法,还是一样的进来先加锁,然后判断元素位置是否越界,再判断新增的位置是刚好最后面还是中间,最后设置新元素再解锁。

总结


CWList核心原理就在于新增元素的会复制一个数组出来操作,不操作原数组,解耦了读操作和写操作,使得读不会与写发生争抢问题。