查看完整视频
小黑屋思过中,禁止观看!
评论并刷新后可见

您需要在视频最下面评论并刷新后,方可查看完整视频

积分观看

您支付积分,方可查看完整视频

{{user.role.value}}
付费视频

您支付费用,方可查看完整视频

¥{{user.role.value}}
课程视频

全面深入50W+年薪技能,构建体系化架构师能力


开始学习
会员专享

视频合集

彻底搞懂CopyOnWriteArrayList的实现原理

  • 课程笔记
  • 问答交流

ArrayList是常用的Java数据结构,但是在多线程环境下ArrayList不是线程安全的类。
线程安全的ArrayList有这些:Vector和CopyOnWriteArrayList。

为了助大家掌握好CopyOnWriteArrayList,这节课我会重点讲解以下6点:

1.Copy-On-Write容器介绍

2.CopyOnWriteArrayList的核心实现

3.CopyOnWriteArrayList为什么性能高

4.CopyOnWriteArrayList的使用案例

5.CopyOnWriteArrayList的优缺点

6.CopyOnWriteArrayList的应用场景

Copy-On-Write容器(写入时复制)

Copy-On-Write简称COW,写入时复制,这个技术,准确的说应该是一种思想。
彻底搞懂CopyOnWriteArrayList的实现原理-mikechen的互联网架构

其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。

通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。

这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。

所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

CopyOnWriteArrayList的核心实现

1.核心结构

public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    //序列号
    private static final long serialVersionUID = 8673264195747942595L;
    /**
     * 可重入锁,对数组增删改时,使用它加锁
     */
    final transient ReentrantLock lock = new ReentrantLock();
    /**
     * 存放元素的数组,其实就是本体
     */
    private transient volatile Object[] array;
}

2.add方法

public boolean add(T 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;
        
        // 把原数组引用指向新数组
        setArray(newElements);


        return true;


    } finally {
        lock.unlock();
    }
}

3.get方法

public E get(int index) {
    return get(getArray(), index);
}
  • 内部持有一个ReentrantLock lock = new ReentrantLock();
  • 底层是用volatile transient声明的数组 array
  • 读写分离,写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array
  • COWList 读操作是无锁的
  • COWList 写与写之间是互斥的

CopyOnWriteArrayList为什么并发安全且性能比Vector好?

 

评论交流
  1. 李鸿翼

    CopyOnWriteArrayList 的底层实现(重点谈谈线程安全部分)?
    底层实现采用了一种写时复制的策略,内部数据结构是一个数组,并用volatile进行了修饰,并发读的时候,无锁竞争,其他线程写时采用采用reentrantlock加独占锁,复制一份当前的数组进行操作,操作完后替换当前的数组,由于是volatile修饰的,读线程能及时感知。

    谈谈CopyOnWriteArrayList 的缺点是什么?
    CopyOnWriteArrayList由于在增删改的时候,完全复制当前数组,这样如果数组容量比较大的时候,就会非常消耗内存,另外写的时候会加锁,如果并发写比较高的情况下,也不适合。

    谈谈CopyOnWriteArrayList 与 Vector 比较?
    vector的读写操作方法上加了synchronized,导致多线程下并发读写也会产生锁竞争,相对来说,CopyOnWriteArrayList 读是无锁的,写的时候才会加锁,因此CopyOnWriteArrayList 在读多写少的情况下性能会更高

    最后谈谈CopyOnWriteArrayList 的典型应用场景?
    CopyOnWriteArrayList 是一种写时复制策略,典型应用场景是在读多写少的情况下,并且容量不是很大场合。比如像注册中心中的路由表信息维护,就比较适合这种结构

  2. 路正银

    CopyOnWriteArrayList 的底层实现(重点谈谈线程安全部分)?
    读不加锁,写加锁(ReentrantLock),写的时候是新复制一个数组(容器)出来(读线程无感知,照旧在读老数
    组),写完成之后,会将数组的引用指向新的数组,数组的引用用volatile来修饰,解决线程之间修改可见性的问
    题。
    谈谈CopyOnWriteArrayList 的缺点是什么?
    1、内容占用问题,每次写数据都需要复制一个新的容器出来
    2、是最终一致性,不能保证数据的实时一致性。在写的过程之中,同时存在的读线程还在读原来的数据。
    谈谈CopyOnWriteArrayList 与 Vector 比较?
    Vector是增删改查都加锁(synchronized),COW是只在增删改上加锁(ReentrantLock独占锁),但是读操作不
    加锁,支持并发读。
    最后谈谈CopyOnWriteArrayList 的典型应用场景?
    读多写少的环境
    COW的实现是用内存来换性能,在数组本身占据内存空间较大的情况之下,需要评估使用