核心思想
读写分离,空间换时间,避免为保证并发安全导致的激烈的锁竞争。
关键点
1、CopyOnWrite适用于读多写少的情况,最大程度的提高读的效率;
2、CopyOnWrite是最终一致性,在写的过程中,原有的读的数据是不会发生更新的,只有新的读才能读到最新数据;
3、如何使其他线程能够及时读到新的数据,需要使用volatile变量;
4、写的时候不能并发写,需要对写操作进行加锁;
源码解析
CopyOnWriteArrayList 相对于 ArrayList 线程安全,底层通过复制数组的方式来实现,其核心概念就是: 数据读取时直接读取,不需要锁,数据写入时,需要锁,且对副本进行操作。那么当数据的操作以读取为主时,我们便可以省去大量的读锁带来的消耗。同时为了能让多线程操作List时,一个线程的修改能被另一个线程立马发现,CopyOnWriteList采用了Volatile关键词来进行修饰,即每次数据读取不从缓存里面读取,而是直接从数据的内存地址中读取。
以CopyOnWriteArrayList 的add()操作为例来看
// 这个数组是核心的,因为用volatile修饰了
// 只要把最新的数组对他赋值,其他线程立马可以看到最新的数组
private transient volatile Object[] array;
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;
// 然后把副本数组赋值给volatile修饰的变量
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
结论
总结而言,多线程实现COW实际上就是以空间换取时间使得数据读取时不需要锁。只是减少了读锁的开销,但与常规的多线程操作共享数据的本质没有什么区别。
参考
https://zhuanlan.zhihu.com/p/114547256
https://cloud.tencent.com/developer/article/1821632
https://blog.csdn.net/javageektech/article/details/108114252