一、CopyOnWriteArrayList 出现之前
- ArrayList 和 LinkedList 作为 List 的数组和链表的实现
- 线程安全的 Vector 和 Collections.synchronizedList() 可以使用
- Vector 就是在方法上加 synchronized ,性能很差
- 这几种 List 在迭代期间不允许编辑
- 如果在迭代期间进行添加元素或删除元素等操作 则会抛出 ConcurrentModificationException 异常
二、适用场景
-
读操作可以尽可能地快,而写即使慢一些也没关系
在很多应用场景中,读操作可能会远远多于写操作
-
读多写少
黑名单是最典型的场景 不能被搜索的关键字会被放在一个黑名单当中 当用户搜索时,会检查当前关键字在不在黑名单当中 如果在,则提示不能搜索
三、读写规则
- CopyOnWriteArrayList 读取是完全不用加锁的
- 可以在写入的同时进行读取
四、特点
五、源码分析
/** 可重入锁对象 */
final transient ReentrantLock lock = new ReentrantLock();
/** CopyOnWriteArrayList底层由数组实现,volatile修饰,保证数组的可见性 */
private transient volatile Object[] array;
/**
* 得到数组
*/
final Object[] getArray() {
return array;
}
/**
* 设置数组
*/
final void setArray(Object[] a) {
array = a;
}
/**
* 初始化CopyOnWriteArrayList相当于初始化数组
*/
public CopyOnWriteArrayList() {
setArray(new Object[0]);
}
public boolean add(E e) {
// 加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 得到原数组的长度和元素
Object[] elements = getArray();
int len = elements.length;
// 复制出一个新数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 添加时,将新元素添加到数组中
newElements[len] = e;
// 将volatile Object[] array 的指向替换成新数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
public E get(int index) {
return get(getArray(), index);
}
final Object[] getArray() {
return array;
}
private E get(Object[] a, int index) {
return (E) a[index];
}
网友评论