针对于并发场景下动态数组的选型,可以使用线程安全的列表Vector,Vector是jdk1.0版本就带有的一个线程安全的动态数组类,但是Vector的实现原理之前分析过,对于所有对内部存储数据结构的操作,都添加了同步锁,这样的实现方式,虽然保证了线程的安全性,大并发场景下,却等同于多个线程的串行化执行,效率低下。jdk1.5引入了新的线程安全的列表实现CopyOnWriteArrayList。
一、CopyOnWriteArrayList的实现类图
类图结构和ArrayList完全一致,这个也合理,从CopyOnWriteArrayList名称上看,它是一种特殊的ArrayList。
同ArrayList直接实现了了四个接口如上,其中List提供了基础的添加、删除、遍历操作;
RandomAccess提供了随机访问能力、Cloneable接口,提供了可以被克隆、Serializable接口,提供了可以序列化的能力;
RandomAccess、Cloneable、Serializable都是空接口,这种空接口在Jdk的机制中,这种标记接口被实现后,起到给类打标记的作用,程序在运行期间通过识别标记,实现相应的功能。当然有兴趣了解更多标记接口的实现和运行机制,可以查阅相关其它资料。
接下来,我们通过阅读源码看代码底层的数据结构的实现、扩容机制上的区别、是否线程安全、以及性能优势。
二、源码分析
内部数据结构对象的声明部分:
/** 保护所有增变基因的锁 */
final transient ReentrantLock lock = new ReentrantLock();
/** 私有的 内部数组 被volatile关键字修饰 */
private transient volatile Object[] array;
内部数据结构的声明部分比ArrayList要简单,仅仅一个数组的声明,但是不同的是有一个ReentrantLock锁。
lock:ReentrantLock独占锁,多线程运行下,只有一个线程可以获得这个锁,只有释放锁后其他线程才能获得。
array:存放数据的数组,关键是被volatile修饰了,保证了可见性,也就是一个线程修改后,其他线程立即可见。
看一下构造函数:
/**
* 创建一个空列表
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
/**
* 声明的成员变量,只能被setArray()、getArray()访问
*/
final void setArray(Object[] a) {
array = a;
}
setArray()、getArray()方法,都final关键字修饰,方法无法被重写,确定了内部封装性。
/**
* 获取列表的大小
* @return the number of elements in this list
*/
public int size() {
</