CopyOnWriteArrayList分析

ArrayList并没有考虑并发的情况,在多线程高并发访问ArrayList的情况下,它并不能保证线程安全。CopyOnWriteArrayList是ArrayList变种,而且它是线程安全的。CopyOnWriteArrayList的add(), remove(), set()等方法的实现都会创建基础数组的拷贝,并在新创建的数组上实现add, remove, set等修改操作,最后将新数组设置成基础数组。因此,旧的数组并没有被修改,其他线程对旧数组的访问依然是正常的,它不会受到新修改的影响。当然这是需要付出代价的,那就是每当修改容器时都会旧数组的同容全部拷贝一遍,这需要更多的内存,更多的读写数据时间开销,特别是当容器规模较大的时候。所以,CopyOnWriteArrayList适用于读操作远远多于写操作的场景,在这种情况下它能够提供更好的并发性能。另外,当你不能或者不想对遍历同步但又希望防止多线程并发的干扰时,CopyOnWriteArrayList就很有用了。

下面分析一下CopyOnWriteArrayList的主要方法:

 

get()方法

 

    private E get(Object[] a, int index) {
        return (E) a[index];
    }

    public E get(int index) {
        return get(getArray(), index);
    }
 get()方法不需要加锁,它的实现也非常简单,只是调用了一个方法get(Object[] a, int index),获取基础数组index位置上的元素。

 

 

add()方法

 

public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

 这是带两个参数的add()方法,作用是在index的位置上插入一个element。由于这是修改操作,为了保证写操作是线程安全的,必须先获得锁。这里使用的是ReentrantLock的lock()方法获取锁。接着检查index的范围,如果超出了正常的范围,就抛出IndexOutOfBoundsException。如果index是指向最后一个元素,则将基础数组的所有元素复制到长度为len+1新数组;如果index指向中间的元素,则先把index前面的所有元素复制到新数组,空出新数组上index所指的位置用于容纳新元素,然后把index之后的所有元素复制到新数组。最后,在新数组的index位置添加element,将新数组设置为基础数组,释放锁。

 

CopyOnWriteArrayList的其他写操作set(), remove()等与add()方法的实现方式基本上是一致的,都需要获得ReentrantLock,复制底层数组到新数组,在新数组上进行修改,最后将新数组设为基础数组,释放锁。

 

CopyOnWriteArrayList内部提供了COWIterator。虽然它实现了ListIterator接口,但是它其他并不支持Iterator上的remove(), add()和set()操作,这些方法只是简单地抛出UnsupportedOperationException。COWIterator初始化的时候就获取了CopyOnWriteArrayList底层的基础数组,Iterator在遍历的过程中不支持对这个基础数组的修改,而其他线程在执行写操作的时候会将基础数组复制一遍,在新的基础数组上进行写操作。因此,无论其他线程执行哪种操作,COWIterator一完成初始化,它的基础数组拷贝就不会发生变化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值