专栏首页晏霖CopyOnWriteArrayList实现原理及源码分析

CopyOnWriteArrayList实现原理及源码分析

CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet。本文会对CopyOnWriteArrayList的实现原理及源码进行分析。

实现原理

我们都知道,集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但由于简单粗暴的锁同步机制,性能较差。而CopyOnWriteArrayList则提供了另一种不同的并发处理策略(当然是针对特定的并发场景)。

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

优缺点分析

了解了CopyOnWriteArrayList的实现原理,分析它的优缺点及使用场景就很容易了。

优点

读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。Java的list在遍历时,若中途有别的线程对list容器进行修改,则会抛出ConcurrentModificationException异常。而CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的list容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常了

缺点

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

源码分析

基本原理了解了,CopyOnWriteArrayList的代码实现看起来就很容易理解了。

public boolean add(E e) {
        //ReentrantLock加锁,保证线程安全
        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();
        }
    }

添加的逻辑很简单,先将原容器copy一份,然后在新副本上执行写操作,之后再切换引用。当然此过程是要加锁的。

删除操作

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)
                //如果要删除的是列表末端数据,拷贝前len-1个数据到新副本上,再切换引用
                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的读操作是不用加锁的,性能很高。

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

直接读取即可,无需加锁

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

总结

本文对CopyOnWriteArrayList的实现原理和源码进行了分析,并对CopyOnWriteArrayList的优缺点也进行了分析(Java并发包中还提供了CopyOnWriteSet,原理类似)。其实所谓并发容器的优缺点,无非是取决于我们在面对特定并发场景时,是否能做出相对合理的选择和应用。也希望本文能帮助到有需要的童鞋,共勉。

本文分享自微信公众号 - 晏霖(yanlin199507),作者:chenxiao

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 策略模式

    策略设计模式难易程度应该是仅次于单例模式的,无论是实现起来还是从结构上看,都非常简单,他跟简单工厂都一部分思想类似,大家可以结合一下简单工厂来看策略模式,大家千...

    胖虎
  • 【排序算法】-快排算法

    笔者也是近期猜对算法感兴趣的,可能对刚入门的同学来说,算法接触不到,但是对于有一些经验的程序员来说,算法的技能是必备的,尤其是面试的时候,动不动就让你手写算法,...

    胖虎
  • Java并发之-读写锁ReentrantReadWriteLock

    之前提到的ReentrantLock是排他锁,这种锁同一时刻只允许一个线程访问,而读写锁同一时刻可以多个线程访问,但在写线程访问时,所有读线程和其他写线程都要被...

    胖虎
  • SaaS公司:如何在抄袭对手被抄袭时找到应对之策?

    编译:张苏月 编者按:模仿抄袭已经成了 SaaS 公司之间的潜规则,但是模仿是不可避免的,被模仿也是必然会发生的,所以 SaaS 厂商需要做好准备。一旦发现自己...

    人称T客
  • 「 互联网笔试题 」No.1

    再开一个系列吧,「互联网笔试题」系列。最近面试也做过不少的题目,分享一波。与「每日一题」系列相同,将会在下一期公布答案与解析。 对了,提醒一下,这是笔试题,请大...

    KEN DO EVERTHING
  • freeRTOS-&OpenRTOS-&SafeRTOS

    在很早之前我们就曾在公众号里给大家介绍过freeRTOS,并且还介绍过在NXP kenitis KV46上的移植,相信很多猿友应该还有印象,freeRTOS因其...

    用户1605515
  • 探秘|大数据揭我国高校毕业生就业的十个主要特点

    近年来,我国经济发展呈现出新常态,主要表现为三个特点:一是经济增长从高速转为中高速;二是产业结构优化升级;三是经济增长模式从要素驱动转向创新驱动。从经济增长来...

    灯塔大数据
  • iOS应用程序的脱壳实现原理浅析

    对于诸多逆向爱好者来说,给一个app脱壳是一项必做的事情。基于安全性的考虑,苹果对上架到appstore的应用都会进行加密处理,所以如果直接逆向一个从appst...

    欧阳大哥2013
  • 大数据Python:3大数据分析工具

    在这篇文章中,我们将讨论三个令人敬畏的大数据Python工具,以使用生产数据提高您的大数据编程技能。

    February
  • 一次访问问题排查-涉及TNS-03505、ORA-12154、TNS-12560、动态注册、防火墙、tnsping跟踪等

    建了一个库,想通过Oracle Net访问,需要配置监听器和tnsnames.ora,接下来碰到一系列的问题。。。 1. 添加监听器配置,listene...

    bisal

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动