Java常用并发容器总结(三)

时间:2022-07-24
本文章向大家介绍Java常用并发容器总结(三),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

CopyOnWriteArrayList

1.介绍

CopyOnWriteArrayList可以理解为一个线程安全的List,它的实现原理是:在写入操作时,进行一次自我复制。也就是说,对于CopyOnWriteArrayList的读操作,不加任何同步处理;当需要进行修改操作时,对原有的数据进行一次复制,将修改的内容写入副本中。写完之后,再用修改后的副本替换原来的数据。这样一来,写操作与读操作之间互不影响。CopyOnWriteArrayList的读-读操作和读-写操作之间都是可以并发执行的,只有在写-写操作之间才互斥。

2.代码分析

首先看 private volatile transient Object[] array;有关读取的操作,以get()方法为例:

 public E get(int index) {
        return get(getArray(), index);
    }
final Object[] getArray() {
        return array;
    }
 private volatile transient Object[] array;

CopyOnWriteArrayList内部使用一个Object[] array数组保存数据,get()方法直接获取array数组index下标处的元素,没有采用任何同步控制和锁操作,由于内部数组不会发生修改,只能被替换,因此可以保证线程安全。

下面再看修改操作的代码,以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();
        }
    }

可以看出,整个修改操作都是在副本数组上进行的,对读取操作不会产生影响。而且,由于内部数组使用volatile关键字修饰,所以数组修改后可以立即被读线程察觉。

3.适用场景

对于读操作远远大于写操作的情况,使用CopyOnWriteArrayList可以很大程度地提高效率。但是如果写操作较多,CopyOnWriteArrayList会频繁地进行内部数组的复制,效率较低。