JUC源碼分析-集合篇(二):CopyOnWriteArrayList和CopyOnWriteArraySet

CopyOnWriteArrayList 是一個(gè)線(xiàn)程安全的 ArrayList,通過(guò)內(nèi)部的 volatile 數(shù)組和顯式鎖 ReentrantLock 來(lái)實(shí)現(xiàn)線(xiàn)程安全。而 CopyOnWriteArraySet 是線(xiàn)程安全的 Set,它是由 CopyOnWriteArrayList 實(shí)現(xiàn),內(nèi)部持有一個(gè) CopyOnWriteArrayList 引用,所有的操作都是由 CopyOnWriteArrayList 來(lái)實(shí)現(xiàn)的,區(qū)別就是 CopyOnWriteArraySet 是無(wú)序的,并且不允許存放重復(fù)值。由于是一個(gè)Set,所以也不支持隨機(jī)索引元素。本章我們重點(diǎn)介紹 CopyOnWriteArrayList。

和 ArrayList 或 Set 相比,CopyOnWriteArrayList / CopyOnWriteArraySet 擁有以下特性:

  1. 適合元素比較少,并且讀取操作高于更新(add/set/remove)操作的場(chǎng)景
  2. 由于每次更新需要復(fù)制內(nèi)部數(shù)組,所以更新操作開(kāi)銷(xiāo)比較大
  3. 內(nèi)部的迭代器 iterator 使用了“快照”技術(shù),存儲(chǔ)了內(nèi)部數(shù)組快照, 所以它的 iterator 不支持remove、set、add操作,但是通過(guò)迭代器進(jìn)行并發(fā)讀取時(shí)效率很高。

源碼解析

核心參數(shù)
//鎖
final transient ReentrantLock lock = new ReentrantLock();

//用于存儲(chǔ)元素的內(nèi)部數(shù)組
private transient volatile Object[] array;

CopyOnWriteArrayList 實(shí)現(xiàn)非常簡(jiǎn)單。內(nèi)部使用了一個(gè) volatile 數(shù)組(array)來(lái)存儲(chǔ)數(shù)據(jù),保證了多線(xiàn)程環(huán)境下的可見(jiàn)性。在更新數(shù)據(jù)時(shí),都會(huì)新建一個(gè)數(shù)組,并將更新后的數(shù)據(jù)拷貝到新建的數(shù)組中,最后再將該數(shù)組賦值給 array。正由于這個(gè)原因,涉及到數(shù)據(jù)更新的操作效率很低。

由于 CopyOnWriteArrayList 源碼比較簡(jiǎn)單,內(nèi)部都是對(duì)數(shù)組的操作,所以咱們這里以add方法為例,其他方法就不一一分析了。

add(int index, E element)
public void add(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: "+index+
                    ", Size: "+len);
        Object[] newElements;
        //計(jì)算偏移量
        int numMoved = len - index;
        if (numMoved == 0)
            //作為add(E)處理
            newElements = Arrays.copyOf(elements, len + 1);
        else {
            newElements = new Object[len + 1];
            //調(diào)用native方法根據(jù)index拷貝原數(shù)組的前半段
            System.arraycopy(elements, 0, newElements, 0, index);
            //拷貝后半段
            System.arraycopy(elements, index, newElements, index + 1,
                    numMoved);
        }
        newElements[index] = element;
        setArray(newElements);
    } finally {
        lock.unlock();
    }
}

//System中arrayCopy的實(shí)現(xiàn)
public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

說(shuō)明: 還是那句話(huà),非常簡(jiǎn)單,通過(guò)add()方法就可以看出整個(gè) CopyOnWriteArrayList 的實(shí)現(xiàn)就是操作內(nèi)部數(shù)組。首先通過(guò)lock加鎖,新建一個(gè)原數(shù)組長(zhǎng)度加1的新數(shù)組,將原數(shù)組(array)的數(shù)據(jù)拷貝到新數(shù)組中,如果給定索引(index)不是原數(shù)組最后一個(gè)索引,就分兩部分拷貝, 然后將給定元素放到新數(shù)組中給定索引處;最后,將新數(shù)組賦值給array。

小結(jié)

在整個(gè)java.util.concurrent框架里,這兩兄弟可以說(shuō)是最簡(jiǎn)單的兩個(gè)類(lèi)了。操作直觀,沒(méi)有復(fù)雜的運(yùn)算邏輯。本章重點(diǎn):CopyOnWriteArrayList 是通過(guò)拷貝數(shù)組來(lái)實(shí)現(xiàn)內(nèi)部元素操作的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容