CopyOnWrite 介紹
Copy-On-Write
簡稱COW
,是一種程式設計中的優化策略,其實現思路是大家都在共享一個內容,當有人想要修改內容的時候,就建立一個改內容的副本,對副本進行修改,然後再將原本的引用指向副本,完成內容的修改。是一種讀寫分離的併發策略,也是一種延時惰性策略
Java 中的 CopyOnWrite 容器
CopyOnWrite
容器,即寫時複製容器。我們都知道,在 Java 集合中,ArrayList
是非執行緒安全的,Vector
雖然是執行緒安全的,但是Vector
是通過對所有操作都加鎖的方式實現執行緒安全的,效能較差。從 JDK1.5 開始,Java 併發包提供了兩個實現了Copy-On-Write
的容器,分別是CopyOnWriteArrayList
和CopyOnWriteArraySet
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;
}
複製程式碼
可見,CopyOnWriteArrayList
對add(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
容器