天天看點

Java基礎——CopyOnWriteArrayList源碼分析

CopyOnWriteArrayList是什麼

  1. CopyOnWriteArrayList是List接口的同步實作
  2. CopyOnWriteArrayList是基于數組複制的操作,對于增、删、改的操作開銷很大
  3. CopyOnWriteArrayList使用疊代器周遊不會導緻與其他線程發生沖突,依賴于不變的數組快照
public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}
           
  1. CopyOnWriteArrayList實作了List接口、底層使用數組儲存所有元素,其操作基本上是對數組的操作
  2. CopyOnWriteArrayList實作了RandmoAccess接口,即提供了随機通路功能,RandmoAccess是java中用來被List實作,為List提供快速通路功能的,我們可以通過元素的序号快速擷取元素對象,這就是快速随機通路
  3. CopyOnWriteArrayList實作了Cloneable接口,即覆寫了函數clone(),能被克隆
  4. CopyOnWriteArrayList實作了java.io.Serializable接口,意味着ArrayList支援序列化

CopyOnWriteArrayList成員變量

//用于寫操作的鎖
final transient Object lock = new Object();

//用于儲存數組的數組,volatile用于線程的可見性
private transient volatile Object[] elements;
           

CopyOnWriteArrayList構造方法

CopyOnWriteArrayList一共提供了3個構造方法

  1. public CopyOnWriteArrayList(E[] toCopyIn):用指定的數組,複制到目前清單中
  2. public CopyOnWriteArrayList(Collection<? extends E> c):用指定的清單,複制到目前清單中
  3. public CopyOnWriteArrayList():預設構造函數,建立一個空清單

CopyOnWriteArrayList的存儲

public boolean add(E e) {
    //1、先加鎖
    synchronized (lock) {
        Object[] elements = getArray();
        int len = elements.length;
        //2、拷貝數組,長度為原容器長度加一
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //3、将元素加入到新數組中
        newElements[len] = e;
        //4、将array引用指向到新數組
        setArray(newElements);
        return true;
    }
}
           

存儲邏輯主要是将原資料拷貝一份新副本,在新副本上做添加操作,最後再将引用切換到新副本上

CopyOnWriteArrayList的删除

public E remove(int index) {
    //1、先加鎖
    synchronized (lock) {
        //2、擷取目前數組
        Object[] elements = getArray();
        int len = elements.length;
        //3、擷取移除的元素
        E oldValue = get(elements, index);
        int numMoved = len - index - 1;
        if (numMoved == 0)
            //4、如果屬于最後一個元素,則拷貝len-1的元素到新副本上,并切換引用
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            //5、如果不是,則将删除元素之外的其他元素拷貝到新副本中,并切換引用
            Object[] newElements = new Object[len - 1];
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
            setArray(newElements);
        }
        return oldValue;
    }
}
           

删除邏輯主要是數組的操作,同存儲邏輯差不多,都是拷貝新副本後再切換引用的操作

CopyOnWriteArrayList的擷取

public E get(int index) {
    return get(getArray(), index);
}

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

final Object[] getArray() {
    return elements;
}
           

擷取的邏輯主要是數組的快速通路,性能很高,在高并發的情況下,無法保證其他線程對副本的操作後再切換給原資料

在多線程并發的情況下,get操作還是會擷取到髒資料

CopyOnWriteArrayList優缺點

1、優點

  1. 讀操作性能很高,無需同步操作,比較适用于讀多寫少的并發場景
  2. 在疊代器周遊的時候,保證線程是安全的,不會抛出線程不安全的錯誤異常

2、缺點

  1. 記憶體占用問題,由于寫操作的時候,需要拷貝數組,會消耗記憶體,在原數組較多的情況下,容易導緻gc操作
  2. 無法保證明時性,不能用于實時讀的場景,由于get操作是擷取原資料,無法保證其他線程對副本的操作後再切換給原資料

CopyOnWriteArrayList總結

  1. 讀寫分離,适用于讀多寫少的場景
  2. 用拷貝副本的思想來解決并發沖突
  3. 無法保證明時性

繼續閱讀