1. 程式人生 > 其它 >【多執行緒】JUC版的CopyOnWriteArrayList

【多執行緒】JUC版的CopyOnWriteArrayList

CopyOnWriteArrayList

  • CopyOnWriteArrayList適合於多執行緒場景下使用,其採用讀寫分離的思想,讀操作不上鎖,寫操作上鎖,且寫操作效率較低。
  • CopyOnWriteArrayList基於fail-safe機制,每次修改都會在原先基礎上複製一份,修改完畢後在進行替換
  • CopyOnWriteArrayList採用的是ReentrantLock進行上鎖。
  • CopyOnWriteArrayList和ArrayList一樣,其底層資料結構也是陣列,加上transient不讓其被序列化,加上volatile修飾來保證多執行緒下的其可見性和有序性。
  • CopyOnWriteArrayList效率比ArrayList低
    不少,畢竟多執行緒場景下,其每次都是要在原陣列基礎上覆制一份在操作耗記憶體和時間,而ArrayList只是容量滿了進行擴容,因此在非多執行緒的場景下還是用ArrayList吧。

建構函式

public CopyOnWriteArrayList() {
    //預設建立一個大小為0的陣列
    setArray(new Object[0]);
}

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

public CopyOnWriteArrayList(Collection<? extends E> c) {
    Object[] elements;
    //如果當前集合是CopyOnWriteArrayList的型別的話,直接賦值給它
    if (c.getClass() == CopyOnWriteArrayList.class)
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        //否則呼叫toArra()將其轉為陣列   
        elements = c.toArray();
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elements.getClass() != Object[].class)
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
    }
    //設定陣列
    setArray(elements);
}

public CopyOnWriteArrayList(E[] toCopyIn) {
    //將傳進來的陣列元素拷貝給當前陣列
    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));
}

讀取方法(未加鎖

final Object[] getArray() {
    return array;
}
public int size() {
    return getArray().length;
}
public boolean isEmpty() {
    return size() == 0;
}
public int indexOf(E e, int index) {
    Object[] elements = getArray();
    return indexOf(e, elements, index, elements.length);
}
public int lastIndexOf(Object o) {
    Object[] elements = getArray();
    return lastIndexOf(o, elements, elements.length - 1);
}

........

add方法(使用ReentrantLock加鎖

public boolean add(E e) {
    //使用ReentrantLock上鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //呼叫getArray()獲取原來的陣列
        Object[] elements = getArray();
        int len = elements.length;
        //複製老陣列,得到一個長度+1的陣列
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //新增元素,在用setArray()函式替換原陣列
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

可見其修改操作是基於fail-safe機制,像我們的String一樣,不在原來的物件上直接進行操作,而是複製一份對其進行修改,另外此處的修改操作是利用Lock鎖進行上鎖的,所以保證了執行緒安全問題。

remove方法(使用ReentrantLock加鎖

public boolean remove(Object o) {
    Object[] snapshot = getArray();
    int index = indexOf(o, snapshot, 0, snapshot.length);
    return (index < 0) ? false : remove(o, snapshot, index);
}

private boolean remove(Object o, Object[] snapshot, int index) {
    final ReentrantLock lock = this.lock;
    //上鎖
    lock.lock();
    try {
        Object[] current = getArray();
        int len = current.length;
        if (snapshot != current) findIndex: {
            int prefix = Math.min(index, len);
            for (int i = 0; i < prefix; i++) {
                if (current[i] != snapshot[i] && eq(o, current[i])) {
                    index = i;
                    break findIndex;
                }
            }
            if (index >= len)
                return false;
            if (current[index] == o)
                break findIndex;
            index = indexOf(o, current, index, len);
            if (index < 0)
                return false;
        }
        //複製一個數組
        Object[] newElements = new Object[len - 1];
        System.arraycopy(current, 0, newElements, 0, index);
        System.arraycopy(current, index + 1,
                         newElements, index,
                         len - index - 1);
        //替換原陣列
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

思路與add方法一致。