CopyOnWrite
CopyOnWrite
- CopyOnWrite简称COW,是一种程序设计中的优化策略。
- JDK里COW容器有两种:
- CopyOnWriteArrayList
- CopyOnWriteArraySet
- COW容器非常有用,可以在非常多的并发场景中使用
- 使用场景,读多写少的时候,容量不大时(Copy操作) 比如缓存
- 属于读写分离的思想,读和写不同的容器。
CopyOnWrite
写时复制的容器
- 通俗的讲就是当我们往容器里面添加元素的时候,不直接在当前容器添加,二十先将当前容器进行Copy,复制出一个新的容器,然后往新的容器里面添加元素。
- 这样做的好处是,我们可以对CopyOnwrite并发的读,而不需要加上,因为不添加任何元素。
源码分析
写操作:add()
private transient volatile Object[] array;
final void setArray(Object[] a) {
array = a;
}
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();
}
}
- 使用了重入锁,加入有一个线程A,一个线程B,如果线程A正在写,那么线程锁住,等它写完之后线程B才可以写。
- getArray()获取当前容器,将当前容器复制到新的容器,再将新的容器复制到当前容器。JVM回收原来的容器。
set操作
同样更新的是一个副本,更新之后再覆盖原来的值,保证了读写分离。
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len);
newElements[index] = element;
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
lock.unlock();
}
}
总结
注意:
- 减少扩容开销。根据实际需要,初始化CopyOnWriteMap的大小,避免写时CopyOnWriteMap扩容的开销。
- 使用批量添加。因为每次添加,容器每次都会进行复制,所以减少添加次数,可以减少容器的复制次数。如使用上面代码里的addBlackList方法
缺点:
- 内存占用问题
- 针对内存占用问题,可以通过压缩容器中的元素的方法来减少大对象的内存消耗,比如,如果元素全是10进制的数字,可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器,而使用其他的并发容器
- 数据一致性问题
- CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。

浙公网安备 33010602011771号