Java 中的 CopyOnWrite

huangxy發表於2020-03-20

CopyOnWrite 介紹

Copy-On-Write簡稱COW,是一種程式設計中的優化策略,其實現思路是大家都在共享一個內容,當有人想要修改內容的時候,就建立一個改內容的副本,對副本進行修改,然後再將原本的引用指向副本,完成內容的修改。是一種讀寫分離的併發策略,也是一種延時惰性策略

Java 中的 CopyOnWrite 容器

CopyOnWrite容器,即寫時複製容器。我們都知道,在 Java 集合中,ArrayList是非執行緒安全的,Vector雖然是執行緒安全的,但是Vector是通過對所有操作都加鎖的方式實現執行緒安全的,效能較差。從 JDK1.5 開始,Java 併發包提供了兩個實現了Copy-On-Write的容器,分別是CopyOnWriteArrayListCopyOnWriteArraySet

CopyOnWriteArrayList 實現原理

下面通過分析CopyOnWriteArray的原始碼,瞭解CopyOnWriteArrayList是如何實現的

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();
    }
}

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

final void setArray(Object[] a) {
    array = a;
}
複製程式碼

可見,CopyOnWriteArrayListadd(E e)方法加了同步鎖,通過Arrays.copyOf()方法,複製出一個長度為原陣列的副本,然後將元素新增到新陣列裡,最後呼叫settArray方法,將原陣列的引用指向新陣列

public E get(int index) {
	return get(getArray(), index);
}
複製程式碼

讀取資料的時候並沒有枷鎖,如果讀的時候正好有其他執行緒在修改CopyOnWriteArrayList的話,讀到的還是舊的資料,因為這時候並不會鎖住CopyOnWriteArrayList

JDK 中並沒有實現CopyOnWriteMap,如果需要可以參照CopyOnWriteArrayList實現一個

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {
    private volatile Map<K, V> internalMap;
 
    public CopyOnWriteMap() {
        internalMap = new HashMap<K, V>();
    }
 
    public V put(K key, V value) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            V val = newMap.put(key, value);
            internalMap = newMap;
            return val;
        }
    }
 
    public V get(Object key) {
        return internalMap.get(key);
    }
 
    public void putAll(Map<? extends K, ? extends V> newData) {
        synchronized (this) {
            Map<K, V> newMap = new HashMap<K, V>(internalMap);
            newMap.putAll(newData);
            internalMap = newMap;
        }
    }
}
複製程式碼

CopyOnWrite 優缺點

優點:

  • 讀取效能很高,因為讀取的時候是無鎖的,比較適合讀多寫少的場景
  • 採用讀寫分離策略,允許讀取的時候修改集合資料,沒有 fail-fast 機制

缺點:

  • 記憶體佔用問題,因為CopyOnWrite的寫時複製機制,當進行寫操作,同時又有執行緒在讀取資料的時候,記憶體裡就會同時駐紮兩個物件的記憶體,如果這些物件佔用的記憶體比較大,比如說200M左右,那麼再寫入100M資料進去,記憶體就會佔用300M,那麼這個時候很有可能造成頻繁的Yong GC和Full GC
  • 資料一致性問題,CopyOnWrite只保證資料的最終一致性,並不能保證資料的實時一致性。所以對資料實時一致性要求比較高的場景不適合使用CopyOnWrite容器
掃碼關注我
一起學習,一起進步

Java 中的 CopyOnWrite