CopyOnWriteArrayList是ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。
其原理是: 初始化的时候只有一个容器,很长一段时间,这个容器数据、数量等没有发生变化,多个线程都是读取同一个容器中的数据,所以这样大家读到的数据都是唯一、一致、安全的,如果后来有线程往里面增加了一个数据,这个时候CopyOnWriteArrayList 底层实现添加的原理是先copy出一个容器(可以简称副本),再往副本容器里添加这个新的数据,最后把副本容器的引用地址赋值给之前旧的的容器地址,但是在添加这个数据的期间,其他线程如果要去读取数据,仍然是读取到旧的容器里的数据。
二、CopyOnWriteArrayList数据结构
通过源码分析可知,CopyOnWriteArrayList使用的数据结构是数组。
CopyOnWriteArrayList
使用了一种叫
写时复制
的方法,当有新元素添加到
CopyOnWriteArrayList
时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
当有新元素加入的时候,创建新数组,并往新数组中加入一个新元素,这个时候,array这个引用仍然是指向原数组的。
当元素在新数组添加成功后,将array这个引用指向新数组。
CopyOnWriteArrayList
的整个add操作都是在
锁
的保护下进行的。
这样做是为了避免在多线程并发add的时候,
复制出多个副本出来
,把数据搞乱了,导致最终的数组数据不是我们期望的。
三、CopyOnWriteArrayList源码分析
3.1 类的继承关系
public
class
CopyOnWriteArrayList<E>
implements
List<E>, RandomAccess, Cloneable, java.io.Serializable {}
说明:CopyOnWriteArrayList实现了List接口,List接口定义了对列表的基本操作;同时实现了RandomAccess接口,表示可以随机访问(数组具有随机访问的特性);同时实现了Cloneable接口,表示可克隆;同时也实现了Serializable接口,表示可被序列化。
3.2 类的内部类
1. COWIterator类
static
final
class
COWIterator<E>
implements
ListIterator<E>
{
/**
Snapshot of the array
*/
//
快照
private
final
Object[] snapshot;
/**
Index of element to be returned by subsequent call to next.
*/
//
游标
private
int
cursor;
//
构造函数
private
COWIterator(Object[] elements,
int
initialCursor) {
cursor
=
initialCursor;
snapshot
=
elements;
}
//
是否还有下一项
public
boolean
hasNext() {
return
cursor <
snapshot.length;
}
//
是否有上一项
public
boolean
hasPrevious() {
return
cursor > 0
;
}
//
next项
@SuppressWarnings("unchecked"
)
public
E next() {
if
(! hasNext())
//
不存在下一项,抛出异常
throw
new
NoSuchElementException();
//
返回下一项
return
(E) snapshot[cursor++
];
}
@SuppressWarnings(
"unchecked"
)
public
E previous() {
if
(!
hasPrevious())
throw
new
NoSuchElementException();
return
(E) snapshot[--
cursor];
}
//
下一项索引
public
int
nextIndex() {
return
cursor;
}
//
上一项索引
public
int
previousIndex() {
return
cursor-1
;
}
/**
* Not supported. Always throws UnsupportedOperationException.
*
@throws
UnsupportedOperationException always; {
@code