CopyOnWriteArrayList源码剖析

1.1前言

        CopyOnWriteArrayList是JAVA中的并发容器类,同时也是符合写时复制思想的CopyOnWrite容器。写时复制思想便是当咱们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,而后新的容器里添加元素,添加完元素以后,再将原容器的引用指向新的容器。这样作的好处是咱们能够对CopyOnWrite容器进行并发的读,而不须要加锁,由于当前容器不会添加任何元素。因此CopyOnWrite容器也是一种读写分离的思想,读和写不一样的容器。java

1.2源码分析

1.2.1实现接口

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

    CopyOnWriteArrayList实现了List和RandomAccess接口,使得该容器能够具备列表的基本功能和随机访问的特性,而且实现了Cloneable接口和Serializable接口,表示可被克隆和序列化。数组

1.2.2成员变量

//重入锁
final transient ReentrantLock lock = new ReentrantLock();

//对象数组,用于存放数据,用volatile修饰
private transient volatile Object[] array;

1.2.3构造函数

//设置数组
final void setArray(Object[] a) {
    array = a;
}

//调用setArray,建立一个空的列表
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}

//建立一个包含collection的列表
public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;
    //判断集合C的类型是不是CopyOnWriteArrayList,若是是则获取集合的数组,不然进入else
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        elements = c.toArray();//将集合转为数组
        //判断elements的类型是否为Object[]类型,若是不是则转为Object[]类型
        if (elements.getClass() != Object[].class)
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
    }
    setArray(elements);//设置数组
}
//将toCopyIn转为Object[]类型,而后设置数组
public CopyOnWriteArrayList(E[] toCopyIn) {
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

1.2.4get方法

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

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

        get方法没有加锁也没有cas操做,所以代码很是简单。并发

1.2.5add方法

//将指定元素添加到列表尾部
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;//添加元素e
        setArray(newElements);//设置数组
        return true;
    } finally {
        lock.unlock();//释放锁
    }
}

1.2.6set方法

//替换列表指定位置的元素
public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();//加锁
    try {
        Object[] elements = getArray();
        E oldValue = get(elements, index);//获取指定位置的旧值

        if (oldValue != element) {
            int len = elements.length;//旧数组长度
            Object[] newElements = Arrays.copyOf(elements, len);//建立新数组,并将旧数组的数组复制到新数组中
            newElements[index] = element;//替换指定位置的元素
            setArray(newElements);//设置数组
        } else {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);
        }
        return oldValue;//返回旧值
    } finally {
        lock.unlock();//释放锁
    }
}

1.2.7remove方法

//删除指定位置的元素
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)
            setArray(Arrays.copyOf(elements, len - 1));//移动个数为0则表示移除的是数组的最后一个元素,复制elements数组,复制长度为length-1,而后设置数组
        else {//移动个数不为0
            Object[] newElements = new Object[len - 1];//建立一个新数组
            System.arraycopy(elements, 0, newElements, 0, index);//复制index以前的元素
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);//复制index以后的元素
            setArray(newElements);//设置数组
        }
        return oldValue;
    } finally {
        lock.unlock();
    }
}

1.3缺点

  • 内存占用问题,有可能形成频繁的垃圾回收。
  • 数据一致性问题,CopyOnWriteArrayList只能保证数据的最终一致性,不能保证数据的实时一致性。

1.4总结

        对于CopyOnWriteArrayList容器来讲,只适合读多写少的并发场景下使用。dom

相关文章
相关标签/搜索