併發包系列二—— CopyOnWriteArrayList

weixin_34402408發表於2017-03-26

本系列文章所描述的所有類或介面都是基於 JDK 1.7的原始碼,在其它 JDK 的實現方式中可能會有所不同。

一、簡單介紹

CopyOnWriteArrayList 是一個執行緒安全、並且在讀操作時無鎖的 ArrayList,其具體實現方法如下。

二、CopyOnWriteArrayList()

和 ArrayList 不同,此步的做法為建立一個大小為 0 的陣列。

    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

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

三、add(E)

add 方法並沒有加上 synchronized 關鍵字,它通過使用 ReentrantLock 來保證執行緒安全。
  除了使用 ReentrantLock 來保證執行緒安全外,此處和 ArrayList 的不同是每次都會建立一個新的 Object 陣列,此陣列的大小為當前陣列大小加 1,將之前陣列中的內容複製到新的陣列中,並將新增加的物件放入陣列末尾,最後做引用切換將新建立的陣列物件賦值給全域性的陣列物件。

四、remove(E)

和 add 方法一樣,此方法也通過 ReentrantLock 來保證其執行緒安全,但它和 ArrayList 刪除元素採用的方式並不一樣。
  首先建立一個比當前陣列小 1 的陣列,遍歷新陣列,如找到 equals 或均為 null 的元素,則將之後的元素全部賦值給新的陣列物件,並做引用切換,返回 true;如未找到,則將當前的元素賦值給新的陣列物件,最後特殊處理陣列中的最後一個元素,如最後一個元素等於要刪除的元素,則將當前陣列物件賦值為新建立的陣列物件,完成刪除操作,如最後一個元素也不等於要刪除的元素,那麼返回 false。
  此方法和 ArrayList 除了鎖不同外,最大的不同在於其複製過程並沒有呼叫 System 的 arrayCopy 來完成,理論上來說會導致效能有一定下降。

五、get(int)

此方法非常簡單,直接獲取當前陣列對應位置的元素,這種方法是沒有加鎖保護的,因此可能會出現讀到髒資料的現象。但相對而言,效能會非常高,對於寫少讀多且髒資料影響不大的場景而言,CopyOnWriteArrayList 是不錯的選擇。

六、iterator()

呼叫 iterator 方法後建立一個新的 COWIterator 物件例項,並儲存了一個當前陣列的快照,在呼叫 next 遍歷時則僅對此快照陣列進行遍歷,因此遍歷 CopyOnWriteArrayList 時不會丟擲 ConcurrentModificatiedException。
  從以上的分析可見,CopyOnWriteArrayList 基於 ReentrantLock 保證了增加元素和刪除元素動作的互斥。在讀上沒有做任何鎖操作,這樣就保證了讀的效能,帶來的副作用是有些時候可能會讀取到髒資料。

相關文章