- Copy-On-Write
Copy-On-Write是一种程序设计中的优化策略,基本思路是,从一开始,大家都共享同一个内容,当某个人想要修改这个内容的时候,才会正在把内容copy出去,形成一个新的内容,然后再改,这是一种延迟懒惰策略。
- CopyOnWrite容器
CopyOnWrite是写时进行复制的容器。通俗理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器复制到一个新的容器,然后再往新的容器中添元素,再将原容器的引用指向新的容器。
这样做的好处是,可以对CopyOnWrite容器进行并发的读,而不需要加锁。
所以CopyOnWrite容器是一种读写分离的思想,读和写不同的容器。
- CopyOnWriteArrayList 的实现原理
1)add方法:在添加新元素的时候,是需要加锁的。否则多线程同时写会copy出N个副本。
public boolean add(T 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 void setArray(Object[] a) {
array = a;
}
读的时候不需要加锁,如果读的时候有多个线程正在想ArrayList中添加元素,读还会读到旧的数据,因为写的时候不会锁住旧的ArrayList.
读的代码:
public E get(int index) {
return get(getArray(), index);
}
综上,CopyOnWriteArrayList, 不允许多线程一起写,一起写时要排队;但允许写的时候多线程一起读,因为写完后直接指针切换。
了解了Copy-On-Write机制,可以自己实现各种Copy-On-Write容器,如实现个CopyOnWriteMap:
import java.util.Collection;
import java.util.Map;
import java.util.Set;
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的应用场景
CopyOnWrite并发容器用于读多写少的并发场景。
- CopyOnWrite的缺点
1)费内存;
2)数据一致性。
1)内存占用问题:CopyOnWrite写时采用复制机制,所以在写时,内存中会同时有2个对象的内存,即旧的对象和新写入的对象。可能会造成频繁Full GC.
2)数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的数据能够马上独到,不用使用CopyOnWrite容器。