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

相關文章
相關標籤/搜索