前言
以后简称为CWList,它为什么被称为线程安全的数组的呢?
中文名又叫写时复制数组,那就从写开始探索一下它的奥秘把。
结构
从它的结构图可以看到,所有的逻辑都是它自身实现的,没有继承任何类和抽象类。
构造方法
从它的3个构造方法可以看到,CWList是不能指定初始大小长度的。要么就是0长度,要么就传入新数组进来替代容器。
元素容器
可以看到CWList只有2个属性,一个用来加锁的lock,一个用来存放数据的容器 array;
并且array添加了volatile属性,这个属性的作用就是保证各线程对于该属性的可见性以及有序性。
简而言之,只要打上了volatile关键的属性,它的修改就能立即被其他线程'看到'。
add方法
add方法整个过程很简单清晰:
- 加上一把锁,防止其他线程同时操作
- 复制一份原数组长度+1的新数组出来
- 在新数组上进行增加元素
- 设置新数组为新的容器array
- 解锁
到这应该就明白为什么它被称为写时复制数组了,因为它的就是在写的时候复制一份新数组出来操作,从而不影响其他数组的读取,极大的提高了并发能力。
这个又和MySQL的mvcc很像,读取的都是快照数据。
再来看一下另一个add方法又是如何实现的
这个方法就是在指定位置插入元素,我们可以先自己分析一下,如果我们要实现这个方法需要考虑什么?
- 第一个考虑的肯定是位置范围如何定?
- 第二个考虑的是在中间插入怎么处理?
这里CWList给了我们答案;
- 第一个是只能在本身长度 - 本身长度+1的范围内进行新增
- 第二个是中间插入就插入中间位置,后面的元素往后移位。
分析一下该方法,还是一样的进来先加锁,然后判断元素位置是否越界,再判断新增的位置是刚好最后面还是中间,最后设置新元素再解锁。
总结
CWList核心原理就在于新增元素的会复制一个数组出来操作,不操作原数组,解耦了读操作和写操作,使得读不会与写发生争抢问题。