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
容器
