CopyOnWriteArrayList 实现原理

    CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全的ArrayList,写操作通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet,不过在CopyOnWriteSet中任然是调用的是CopyOnWriteArrayList。

实现原理

    集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但是它的锁同步机制十分简单所以性能较差。而CopyOnWriteArrayList则提供了另一种不同的并发处理策略(读写分离)。

 CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();

    /** The array, accessed only via getArray/setArray. */
    private transient volatile Object[] array;


    /**
     * 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;
            //拷贝一份原数组并+1,生成的新数组的len位置上增加元素
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            //替换原数组
            setArray(newElements);
            return true;
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    可以看到它在新增元素的时候会获取锁然后生成一个新的数组+1,这时候再将新数组的新增位置添加新元素,最后替换原数组就行了。

   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;
            //表示是末尾,直接拷贝原数组0-(len-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();
        }
    }

    删除操作同理,把要删除的元素之外的其他元素拷贝到新数组中,然后切换引用,将原数组引用指向新数组。同属写操作,需要加锁。

public E get(int index) {
        return get(getArray(), index);
    }

     获取元素无需加锁。

优缺点

优点:

  1. CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的数组,所以在使用迭代器进行遍历时候,不会抛出ConcurrentModificationException异常。
  2. 读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。

缺点:

  1. 内存占用问题,每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC。
  2. 无法保证实时性,Vector对于读写操作均加锁同步,可以保证读和写的强一致性。而CopyOnWriteArrayList的实现策略是写和读分别作用在新老不同数组上,在写操作执行过程中,读不会阻塞但读取到的却是为更新后的数据。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值