论证 CopyOnWriteArrayList 如何成为线程安全的 ArrayList

> 我不停奔跑只为追赶当年被寄予厚望的自己。
> ——利文斯顿
Q:来,介绍下
ArrayList 的一个线程安全的变体,在这个变体中,所有突变操作(add、set等)都是通过对底层数组的做一份新的拷贝来实现的。
这种方式通常成本太高,但当遍历操作的数量远远超过突变操作时,可能比其他方法更高效,而且当你不能或不想同步遍历,但又需要避免并发线程之间的干扰时,这种方式很有用。“快照” 风格的迭代器方法使用了一个引用来创建迭代器时的数组状态。这个数组在迭代器的生命周期内永远不会改变,所以干扰是不可能的,并且保证迭代器不会抛ConcurrentModificationException。迭代器创建后,不会反映列表的添加、删除或更改。不支持对迭代器本身进行元素改变操作(remove,set,add)。这些方***抛出UnsupportedOperationException。
所有元素都是允许的,包括null。
内存一致性的影响。与其他并发集合一样,在将对象放入CopyOnWriteArrayList(后文简称 COW)之前,线程中的操作会在另一个线程中的CopyOnWriteArrayList中访问或删除该元素的后续操作之前发生。
这个类也是Java集合框架中的一个成员。
Q:那你先说说他具体是怎么保证线程安全的吧?
- 写时复制策略
如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。优点是如果调用者没有修改该资源,就不会有副本(private copy)被建立,因此多个调用者只是读取操作时可以共享同一份资源。
我们必定要从写入手,看看写时到底做了什么,看实现了 List 接口下最传统的 add 方法:
注意在向 COW 里添加元素时是需要加锁的,否则会 copy 出N个副本。
public boolean add(E e) {
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> “挨踢”业行情日益严峻,企业招聘门槛愈来愈高,大厂hc更是少之又少,而Java技术面试普遍对基础知识的掌握考察特别深,大多数同学突击所看的 Java 面试基础知识点根本达不到面试官近乎挑剔的要求。 本专刊针对如今的校招及社招痛点,深入解析 JDK 的核心源码,探究 JDK 的设计精髓及最佳实践,同时以模拟面试的场景切入,让同学们在阅读过程中也能轻松掌握面试技巧。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p>
查看20道真题和解析
360集团公司福利 394人发布
