CopyOnWriteArrayList 源碼閱讀與分析

CopyOnWriteArrayList 源碼閱讀與分析

      CopyOnWriteArrayList是併發包下的一個線程安全、可以實現高併發的ArrayList

首先來看看它的構造方法:

final void setArray(Object[] a) {

       array = a;

    }

 

   /**

    * Creates an empty list.

    */

   public CopyOnWriteArrayList() {

       setArray(new Object[0]);

}

    可以看到,它和ArrayList有一定的不同,它是創建一個大小爲 0 的數組。ArrayList是一個空數組。

 

     下面來看看它的add(E) 方法,進一步瞭解它的實現原理

      顧名思義,它的名字叫CopyOnWriteArrayList(),也就是在進行寫操作時,如add,remove時,會進行復制操作,即創建一個新的比當前數長度大1的新的數組,再把舊的值複製過去。代碼如下所示:

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;

           setArray(newElements);

           return true;

       } finally {

           lock.unlock();

       }

    }

       add 方法並沒有使用 synchronized 關鍵字來實現互斥,而是通過使用ReentrantLock 來進行加鎖操作。可以看到,它的實現比較簡單,首先加鎖,然後創建一個比目前數組長度大1的新數組,再把舊數組中的值複製到新的數組中去。然後再調用setArray方法改變引用,完成add的操作,結束以後,再釋放鎖。

       之所以再每次的修改數組操作(add/remove等)之後,會新建一個數組,修改完畢之後,再將原來的引用指向新的數組。是爲了保證數組被一個線程遍歷時,沒有其他線程對數組進行修改。所以也就不會拋出ArrayList併發情況下出現的ConcurrentModificationException錯誤。

 

 

     下面再來看看get(int) 方法

   先來看看代碼的實現:

 public E get(int index) {

       return get(getArray(), index);

    }


@SuppressWarnings("unchecked")

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

       return (E) a[index];

    }

 

 

       可以看到,非常簡單,直接獲取當前數組對應的位置的元素,但是由於方法沒有進行任何的加鎖操作,所以可能會出現讀到髒數據的現象,這樣可以有比較高的性能,所以在修改少,讀取多的場景下。它開始很好的選擇。

 

 

 

  再來看看remove(E) 方法

   和 add 方法一樣,此方法也通過ReetrantLock 來保證其線程安全。

     首先判斷要刪除的元素是不是最後一個元素:

          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到index-1的數據複製到新的數組,再把index+1到最後一個元素複製到新的數組中,最後再改變引用。完成這些以後,就完成了刪除的操作。

        可以看到CopyOnWriteArrayList沒有像ArrayList一樣的動態擴容機制,而是再每次對數組修改時都會新建一個新的數組,爲了避免ConcurrentModificationException異常,但是這樣也會對性能有一定的影響。而且它並沒有對讀操作進行任何的線程安全的操作,所以可能會出現讀到髒數據的情況。所以當寫操作比較多時,使用CopyOnWriteArrayList個人感覺不是好的選擇。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章