CopyOnWriteArrayList介绍
CopyOnWriteArrayList是一个线程安全的ArrayList,对其进行的修改操作都是在底层的一个复制的数组(快照)上进行的,也就是模仿了写时复制策略
源码分析
初始化:内部创建了一个大小为0的Object数组作为array的初始值
==有参构造函数==
添加元素
- 调用add方法的线程会首先执行代码去获取独占锁,如果多个线程都调用
add
方法则只有一个线程会获取到该锁,其他线程会被阻塞挂起直到锁被释放。 - 所以一个线程获取到锁后,就保证了在该线程添加元素的过程中其他线程不会对array进行修改。
- 线程获取锁后执行代码获取array,然后执行代码复制array到一个新数组(从这里可以知道新数组的大小是原来数组大小增加1,所以
CopyOnWriteArrayList
是无界list),并把新增的元素添加到新数组。 - 然后执行代码使用新数组替换原数组,并在返回前释放锁。由于加了锁,所以整个
add
过程是个原子性
操作。需要注意的是 - 在添加元素时,首先复制了一个
快照
,然后在快照上进行添加
,而不是直接在原来数组上进行
。
修改指定元素E set(int index,E element)
public E set(int index, E element) {
final ReentrantLock lock = l.lock;
lock.lock();
try {
rangeCheck(index);
checkForComodification();
E x = l.set(index+offset, element);
expectedArray = l.getArray();
return x;
} finally {
lock.unlock();
}
}
- 首先获取了独占锁,从而阻止其他线程对
array
数组进行修改,然后获取当前数组,并调用get方法获取指定位置的元素 - 如果指定位置的元素值与新值不一致则创建新数组并复制元素,然后在新数组上修改指定位置的元素值并设置新数组到
array
。 - 如果指定位置的元素值与新值一样,则为了保证
volatile
语义,还是需要重新设置array
,虽然array
的内容并没有改变。
删除元素
- 首先获取独占锁以保证删除数据期间其他线程不能对array进行修改,然后获取数组中要被删除的元素
- 并把剩余的元素复制到新数组,之后使用新数组替换原来的数组,最后在返回前释放锁。
弱一致性的迭代器
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("hello");
list.add("alibaba");
Iterator<String> itr = list.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
}
- 迭代器的
hasNext
方法用于判断列表中是否还有元素,next方法则具体返回元素。
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
static final class COWIterator<E> implements ListIterator<E> {
// array的快照版本
private final Object[] snapshot;
// 数组下标
private int cursor;
// 构造函数
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
// 是否遍历
public boolean hasNext() {
return cursor < snapshot.length;
}
// 获取元素
@SuppressWarnings("unchecked")
public E next() {
if (! hasNext())
throw new NoSuchElementException();
return (E) snapshot[cursor++];
}
·················································
}
}
- 如果在该线程使用返回的迭代器遍历元素的过程中,其他线程没有对list进行增删改,那么snapshot本身就是list的array
- 因为它们是引用关系。但是如果在遍历期间其他线程对该list进行了增删改,那么snapshot就是快照了,- 因为增删改后list里面的数组被新数组替换了,这时候老数组被
snapshot
引用。这也说明获取迭代器后 - 使用该迭代器元素时,其他线程对该list进行的增删改不可见,因为它们操作的是两个不同的数组,这就是弱一致性。
输出结果
hello
GG/MM
Welcome
to
xiaoff zone
- main函数首先初始化了
arrayList
,然后在启动线程前获取到了arrayList
迭代器。 - 子线程threadOne启动后首先修改了
arrayList
的第一个元素的值,然后删除了arrayList
中下标为2
和3
的元素。 - 主线程在子线程执行完毕后使用获取的迭代器遍历数组元素,从输出结果我们知道,在子线程里面进行的操作一个都没有生效,这就是迭代器弱一致性的体现。
- 需要注意的是,获取迭代器的操作必须在子线程操作之前进行。
今天的文章并发List中的CopyOnWriteArrayList分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/21156.html