Java 高频面试题:聊一聊 JUC 下的 CopyOnWriteArrayList

栏目: IT技术 · 发布时间: 5年前

内容简介:ArrayList 是我们常用的工具类之一,但是在多线程的情况下,ArrayList 作为共享变量时,并不是线程安全的。主要有以下两个原因:如果我们想在多线程情况下使用 ArrayList 怎么办?有以下几种办法:先来看看 SynchronizedLis,Collections 其实就是对 ArrayList 进行了一个加锁包装,这个从源码中可以看出;

ArrayList 是我们常用的 工具 类之一,但是在多线程的情况下,ArrayList 作为共享变量时,并不是线程安全的。主要有以下两个原因:

  • 1、ArrayList 自身的 elementData、size、modCount 在进行操作的时候,都没有加锁

  • 2、 这些变量没有被 volatile 修饰,在多线程的情况下,对这些变量操作可能会出现值被覆盖的情况

如果我们想在多线程情况下使用 ArrayList 怎么办?有以下几种办法:

  • Collections.SynchronizedList

  • JUC 下的 CopyOnWriteArrayList

先来看看 SynchronizedLis,Collections 其实就是对 ArrayList 进行了一个加锁包装,这个从源码中可以看出;

...部分源码,完整源码请查看 JDK 源码...
public void add(int index, E element) {
    synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
    synchronized (mutex) {return list.remove(index);}
}

对于 Collections.SynchronizedList 比较简单,就是锁包装了一下,就不多说了~

CopyOnWriteArrayList 也是 JUC 下面的一个并发容器类。不知道你发现没有,但凡你常用的集合类,在 JUC 下基本上都可以找到一个并发类,比如 hashMap 有对应的 ConcurrentHashMap。

CopyOnWriteArrayList 跟 ArrayList 在整体架构上并没有什么区别,底层都是基于数组实现的 。不同的地方大概有两点:

  • 底层数组被 volatile 关键字修饰;

  • 对数组进行数据变更时加锁;

CopyOnWriteArrayList 的加锁操作跟 Collections.SynchronizedList 简单的加锁还不一样,CopyOnWriteArrayList 中的加锁过程还是非常值得学习的。CopyOnWriteArrayList 的加锁过程,大概可以概括为以下四步:

  • 1、加锁;

  • 2、从原数组中拷贝出新数组;

  • 3、在新数组上进行操作,并把新数组赋值给数组容器;

  • 4、解锁;

结合源码来深入了解 CopyOnWriteArrayList 的并发实现,我们选择 ArrayList 最简单的将元素新增数组尾部的操作来分析实现过程,源码如下:

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
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;
        // 变更底层数组的引用
        setArray(newElements);
        return true;
    } finally {
     // 解锁
        lock.unlock();
    }
}

CopyOnWriteArrayList 就是通过加锁来说实现容器安全的,可能你会有疑问, 「为什么引入一个新数组,数组的拷贝还是消耗时间的,直接在原数组上操作不就好了吗?」 。主要原因有以下两点:

  • volatile 关键字修饰的是数组,如果我们简单的在原来数组上修改其中某几个元素的值,是无法触发可见性的,我们必须通过修改数组的内存地址才行,也就说要对数组进行重新赋值才行

  • 在新的数组上进行拷贝,对老数组没有任何影响,只有新数组完全拷贝完成之后,外部才能访问到,降低了在赋值过程中,老数组数据变动的影响。比如经典的 ConcurrentModificationException 异常问题

其他的新增方法就自己去查看源码了,相差不多,基本上是一样的。对数组的删除跟新增都是差不多,不同的地方是在删除了时候,赋值给新数组时会出现不同的选择策略。我把源码贴上:

public E remove(int index) {
    final ReentrantLock lock = this.lock;
    // 加锁
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        E oldValue = get(elements, index);
        // 先计算出要移动的问题
        int numMoved = len - index - 1;
        // 根据移动的位置选择策略
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            Object[] newElements = new Object[len - 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
            setArray(newElements);
        }
        return oldValue;
    } finally {
       //解锁
        lock.unlock();
    }
}

CopyOnWriteArrayList 还有其他的方法,在这里我就不过多介绍了。根据你们自己的疑问去扒一扒 CopyOnWriteArrayList 的源码就知道了,总体来说 CopyOnWriteArrayList 并不难,甚至感觉比 ArrayList 要简单。

总结一下:CopyOnWriteArrayList 是安全的并发容器,有以下两个特点:

  • 1、对数组的写操作加锁,读操作不加锁;

  • 2、 通过加锁 + 数组拷贝+ volatile 来保证线程安全


以上所述就是小编给大家介绍的《Java 高频面试题:聊一聊 JUC 下的 CopyOnWriteArrayList》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Geometric Folding Algorithms

Geometric Folding Algorithms

Erik D Demaine / Cambridge University Press / 2008-8-21 / GBP 35.99

Did you know that any straight-line drawing on paper can be folded so that the complete drawing can be cut out with one straight scissors cut? That there is a planar linkage that can trace out any alg......一起来看看 《Geometric Folding Algorithms》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具