当前位置: 代码迷 >> 综合 >> JUC-6.2-并发容器-CopyOnWriteArrayList
  详细解决方案

JUC-6.2-并发容器-CopyOnWriteArrayList

热度:68   发布时间:2023-12-05 18:29:15.0

了解了并发安全的 HashMap 之后,再来看看并发安全的 ArrayList ,就是 CopyOnWriteArrayList, 早期的版本中有 VectorSynchronizedList 但是这两个锁的粒度太大,所以并发效率并不高, Copy-On-Write 并发容器还包括 CopyOnWriteArraySet 用来替代同步 Set

适用场景

CopyOnWriteArrayList 适用于读操作尽可能快,写操作可以慢一点的地方,或者读取比写入多的地方

读写规则

CopyOnWriteArrayList 读取完全不需要加锁,写入也不会阻塞读取操作,只有写入和写入之间需要同步等待

设计思想

CopyOnWrite 通俗的来说,就是往容器中添加一个元素的时候,不是直接往当前容器添加,而是先将当前的容器复制一份作为一个新的容器,然后在新的容器中添加,添加完成之后,再将原容器的引用指向新的容器,所以 CopyOnWrite 容器进行并发的读的时候,不需要加锁,因为当前容器不会添加任何元素,但是这样做将不能保证数据的实时一致性

用一个案例来看一下,代码如下

public class ListTest {public static void main(String[] args) {CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>(new Integer[]{1, 2, 3, 4, 5});Iterator<Integer> iterator1 = list.iterator();list.add(6);Iterator<Integer> iterator2 = list.iterator();iterator1.forEachRemaining(System.out::println);iterator2.forEachRemaining(System.out::println);}
}

这段代码的运行结果如下

1
2
3
4
5
1
2
3
4
5
6

可以看到 iterator1 这个迭代器并没有6这元素

实现原理

看一下这个类的源码
在这里插入图片描述

这个类里面维护的就是一个数组 ,构造函数里面创建了一个 Object 长度为0的一个数组。

然后看看 add()get() 方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F2EVrWwC-1587203746642)(http://r.photo.store.qq.com/psc?/V10eEnSd0rz4Am/2aGbA7qLSN6GeC6g0ZsuRX1uye4hkyCJ10XJ.TbpK8nY0..C7lG9ZaksK1G*gTGZ**qzhqK9jzt3iqRRpF8Ass5fWrmge*VYcUnq6XDLfMs!/r)]

首先上锁,保证只有一个线程修改,先复制,改完之后把新的再放到上面的数组中,数组引用是 volatile 修饰的,因此将旧的数组引用指向新的数组,根据 volatilehappens-before 规则,写入线程 对数组引用的修改 读取线程 是可见的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yBc70Mxt-1587203746643)(http://r.photo.store.qq.com/psc?/V10eEnSd0rz4Am/2aGbA7qLSN6GeC6g0ZsuReNSwx1B55IHigZ5y6dmIdQVxWuXa.J.qRkLU8AsdWRnI3iZPVyNWGsoT9VGVDmJg8*6MQ0Zowc.5cYQ1TJXq20!/r)]

get() 方法就很简单了,直接返回

缺点

  1. 不能保证数据一致性
  2. 内存占用,写操作的时候,内存中会有两个对象