JUC----CopyOnWriteArrayList的原理与作用

从字面上看,CopyOnWriteArrayList就是写时复制。当有新元素添加时,首先是将原来的容器复制一份,然后在新容器中进行添加操作,添加完之后,再将指向旧容器的引用给指向新容器。好处是:并发读时不加锁,做到读写分离。

结合源码依次来解析CopyOnWriteArrayList的内容:

1.构造方法:

CopyOnWriteArrayList有三个构造方法,分别是无参构造(创建一个空的),单参构造(传入一个集合或数组)
    final void setArray(Object[] a) {
        array = a;
    }

    /**
    * 创建一个空的数组
    *
    */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

    /**
     * 创建一个包含指定元素的列表,可将传入Collection的实现类
     * 集合,会按照集合返回的顺序
     * 指针
     *
     * @param c 初始保存元素的集合
     * @throws 空指针异常
     */
    public CopyOnWriteArrayList(Collection<? extends E> c) {
        Object[] es;
        if (c.getClass() == CopyOnWriteArrayList.class)
            es = ((CopyOnWriteArrayList<?>)c).getArray();
        else {
            es = c.toArray();
            if (c.getClass() != java.util.ArrayList.class)
                es = Arrays.copyOf(es, es.length, Object[].class);
        }
        setArray(es);
    }

    /**
     * 创建一个包含给定数组副本的列表。
     *
     * @param 在数组中(该数组的副本用作内部数组)
     *        
     * @throws 空指针异常
     */
    public CopyOnWriteArrayList(E[] toCopyIn) {
        setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
    }

2.添加元素

    //使用Object对象作为锁
    final transient Object lock = new Object();  
    
    //一个指向当前容量引用的成员变量(指针)
    private transient volatile Object[] array;  

    final Object[] getArray() {
        return array;
    }

    final void setArray(Object[] a) {
        array = a;
    }

    public boolean add(E e) {
        synchronized (lock) {
            //获取当前列表元素
            Object[] es = getArray();
            //获取当前列表长度
            int len = es.length;
            //将旧数组拷贝到新数组中(新数组长度为旧数组长度+1)
            es = Arrays.copyOf(es, len + 1);
            //在旧长度的位置下添加新元素
            es[len] = e;
            //将新数组重新赋值给成员变量,实际上将指向旧容量的引用改为指向新容量。
            setArray(es);
            //返回修改成功标志
            return true;
        }
    }

3.批量添加

    //批量添加(传入一个集合)
    public boolean addAll(Collection<? extends E> c) {
        //判断其是否是CopyOnWriteArrayList类型,如果是的就调用内部特有方法获取内置数组
        //否则调用集合接口的方法
        Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
            ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
        //数组的长度为0则无需添加,直接返回
        if (cs.length == 0)
            return false;
        //添加前进行加锁
        synchronized (lock) {
            //获取旧容量
            Object[] es = getArray();
            int len = es.length;
            //创建新容量
            Object[] newElements;
            //如果旧容量等于0则无需复制
            if (len == 0 && (c.getClass() == CopyOnWriteArrayList.class ||
                             c.getClass() == ArrayList.class)) {
                newElements = cs;
            } else {  //将旧数组的元素复制到新数组中,同时新数组的长度是len + cs.length
                newElements = Arrays.copyOf(es, len + cs.length);
                //将要复制的数组从0下标开始全部复制到新数组当中
                System.arraycopy(cs, 0, newElements, len, cs.length);
            }
            //修改成员变量的引用
            setArray(newElements);
            //返回成功标志
            return true;
        }
    }

4.获取元素

    //返回指定下标下的数组元素
    static <E> E elementAt(Object[] a, int index) {
        return (E) a[index];
    }

    /**
     * {@inheritDoc}
     *
     * @throws 数组越界异常
     */
    public E get(int index) {
        //传入当前容量数组和想要获取的索引(下标)
        return elementAt(getArray(), index);
    }

5.删除元素

    //传入指定下标并删除该下标的上的元素
    public E remove(int index) {
        //删除前加锁
        synchronized (lock) {
            Object[] es = getArray();
            int len = es.length;
            //记录要被删除的元素值
            E oldValue = elementAt(es, index);
            int numMoved = len - index - 1;
            Object[] newElements;
            if (numMoved == 0)
                //表示删除的元素是最后一个,原数组的位置不动
                newElements = Arrays.copyOf(es, len - 1);
            else {
                //非末尾位置,则删除了该元素之后,其后面的元素需要整体向前移动
                newElements = new Object[len - 1];
                //复制索引前的元素到新数组
                System.arraycopy(es, 0, newElements, 0, index);
                //将旧元素index往后的元素,接着复制到新数组
                System.arraycopy(es, index + 1, newElements, index,
                                 numMoved);
            }
            //修改当前成员变量的引用
            setArray(newElements);
            //将记录的删除元素进行返回
            return oldValue;
        }
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值