java并发系列-CopyOnWriteArrayList
实现原理
CopyOnWriteArrayList容器是写时复制容器,当我们往容器中添加元素的时候,不是直接往当前容器中添加,而是将以前的容器复制一份,将新元素添加到新容器中,添加完成之后会将原容器的引用指向新的容器。这样我们可以对CopyOnWriteArrayList容器可以并发读,不需要添加任何锁。因为你写的是新的容器,读的是老的容器,读写分离互不影响。
相关函数
Add函数
/**
*元素新添加一个记录
*/
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();
//读操作
private E get(Object[] a, int index) {
return (E) a[index];
}
从上面代码我们可以看到以下几点
- 往数组写一个元素的时候需要复制数组,所以大量添加元素的时候肯定性能是比较差
- 读操作和写操作对象不是一个对象,读写分离不需要加锁,读写之间同步处理只需要在写的最后一步将原容器的引用指向新的容器,这个几乎不需要时间,多线程读的时候很适合。
问题
缺点
- 内存占用问题:因为写时复制机制,所以在写的时候,内存中会同时有两个对象的内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc
- 数据一致性问题:只能保证数据的最终一致性,不能保证数据的实时一致性,
- 如果项目中要求写的数据能立马读到,这玩意做不到
优点
- 因为读写分离,没有添加锁,所以适合读多写少的场景,但是我们要注意的一点就是,万一数据稍微有点多,每次add/set都要重新复制数组,这种操作很容易引起故障
闲谈
感觉有帮助的同学还请点赞关注,这将对我是很大的鼓励~,公众号有自己开始总结的一系列文章,需要的小伙伴还请关注下个人公众号程序员fly呀,干货多多,湿货也不少(∩_∩)。
巨人肩膀
https://blog.csdn.net/qq_36930706/article/details/77881449
https://www.pdai.tech/md/java/thread/java-thread-x-juc-collection-CopyOnWriteArrayList.html