CopyOnWriteArrayList底层原理解析 不念不忘少年蓝@ 2022-12-28 08:22 182阅读 0赞 ArrayList并不是一个线程安全的容器,对于高并发下可以用Vector,或者使用Collections的静态方法将ArrayList包装成一个线程安全的类,但是这些方式都是采用Java关键字synchronzied对方法进行修饰,利用独占式锁来保证线程安全的。但是,由于独占式锁在同一时刻只有一个线程能够获取到对象监视器,很显然这种方式效率并不是太高。 集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但由于简单粗暴的锁同步机制,性能较差。而CopyOnWriteArrayList则提供了另一种不同的并发处理策略(当然是针对特定的并发场景:读多写少)。 # 底层原理 # 针对读多写少的并发场景。CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。 **至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。** ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhbmdzaGVuZ3dlaTIzMDYxMg_size_16_color_FFFFFF_t_70] # 源码分析 # CopyOnWriteArrayList.add 添加操作 添加的逻辑很简单,先将原容器copy一份,然后在新副本上执行写操作,之后再切换引用。当然此过程是要加锁的。 public boolean add(E e) { final ReentrantLock lock = this.lock; //1. 使用Lock,保证写线程在同一时刻只有一个 lock.lock(); try { //2. 获取旧数组引用 Object[] elements = getArray(); int len = elements.length; //3. 创建新的数组,并将旧数组的数据复制到新数组中 Object[] newElements = Arrays.copyOf(elements, len + 1); //4. 往新数组中添加新的数据 newElements[len] = e; //5. 将旧数组引用指向新的数组 setArray(newElements); return true; } finally { lock.unlock(); } } add方法的逻辑也比较容易理解,请看上面的注释。需要注意这么几点: 采用ReentrantLock,保证同一时刻只有一个写线程正在进行数组的复制,否则的话内存中会有多份被复制的数据; 前面说过数组引用是volatile修饰的,因此将旧的数组引用指向新的数组,根据volatile的happens-before规则,写线程对数组引用的修改对读线程是可见的。 由于在写数据的时候,是在新的数组中插入数据的,从而保证读写实在两个不同的数据容器中进行操作。 删除操作 删除操作同理,将除要删除元素之外的其他元素拷贝到新副本中,然后切换引用,将原容器引用指向新副本。同属写操作,需要加锁。 public E remove(int index) \{ //加锁 final ReentrantLock lock = this.lock; lock.lock(); try \{ Object\[\] elements = getArray(); int len = elements.length; E oldValue = get(elements, index); int numMoved = len - index - 1; if (numMoved == 0) //如果要删除的是列表末端数据,拷贝前len-1个数据到新副本上,再切换引用 setArray(Arrays.copyOf(elements, len - 1)); else \{ //否则,将除要删除元素之外的其他元素拷贝到新副本中,并切换引用 Object\[\] newElements = new Object\[len - 1\]; //拷贝前半部分 System.arraycopy(elements, 0, newElements, 0, index); //拷贝后半部分 System.arraycopy(elements, index + 1, newElements, index, numMoved); setArray(newElements); \} return oldValue; \} finally \{ //解锁 lock.unlock(); \} \} CopyOnWriteArrayList的读操作是不用加锁的,性能很高。 直接读取即可,无需加锁 public E get(int index) { return get(getArray(), index); } # 优点 # 写的时候不会阻塞读:因为写的时候是拷贝了一个新的数组,往新的里写,读是读旧的。 读的效率很高: 读操作性能很高,因为无需任何同步措施,比较适用于读多写少的并发场景。Java的list在遍历时,若中途有别的线程对list容器进行修改,则会抛出ConcurrentModificationException异常。 而CopyOnWriteArrayList由于其"读写分离"的思想,遍历和修改操作分别作用在不同的list容器,所以在使用迭代器进行遍历时候,也就不会抛出ConcurrentModificationException异常了 # 缺点 # 1. 即内存占用问题:毕竟每次执行写操作都要将原容器拷贝一份,数据量大时,对内存压力较大,可能会引起频繁GC 2. 数据一致性问题:Vector对于读写操作均加锁同步,可以保证读和写的强一致性。,写和读分别作用在新老不同容器上,在写操作执行过程中,读不会阻塞但读取到的却是老容器的数据。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。 [并发容器之CopyOnWriteArrayList详解 ][CopyOnWriteArrayList_] [CopyOnWriteArrayList实现原理及源码分析][CopyOnWriteArrayList] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhbmdzaGVuZ3dlaTIzMDYxMg_size_16_color_FFFFFF_t_70]: /images/20221120/57ad2965288f484ea327a40ebc86d4d7.png [CopyOnWriteArrayList_]: https://blog.csdn.net/ThinkWon/article/details/102508258?utm_source=app [CopyOnWriteArrayList]: https://www.cnblogs.com/chengxiao/p/6881974.html
相关 小米科技笔记 | ElasticSearch与Redis底层原理解析 大家好,我是小米,一个热衷于技术分享的小伙伴!今天,我们来探讨一下两个非常重要的数据存储和检索工具:ElasticSearch和Redis。虽然它们都是高度优化的工具,但在底层 左手的ㄟ右手/ 2024年02月29日 02:50/ 0 赞/ 14 阅读
相关 CopyOnWriteArrayList底层原理 CopyOnWriteArrayList底层原理 CopyOnWriteArrayList是java并发包中的一个非常有用的类,它的特点是线程安全,且在并发环境下读操作无 ╰+攻爆jí腚メ/ 2023年10月14日 12:59/ 0 赞/ 58 阅读
相关 HashMap底层实现原理解析 ![6225b9e118544330a17ef8314bc9e2d0.png][] 一、 HashMap中的put()和get()的实现原理: 1、map.put(k 阳光穿透心脏的1/2处/ 2023年10月13日 11:03/ 0 赞/ 68 阅读
相关 Spring底层核心原理解析 class userServiceProxy extends UserService(){ //生成代理类去继承UserService UserSer 偏执的太偏执、/ 2023年10月03日 22:04/ 0 赞/ 9 阅读
相关 SpringBoot:SpringBoot 的底层运行原理解析 声明原文出处:狂神说 文章目录 1. pom.xml 1 . 父依赖 旧城等待,/ 2023年09月24日 23:35/ 0 赞/ 72 阅读
相关 CopyOnWriteArrayList底层原理解析 ArrayList并不是一个线程安全的容器,对于高并发下可以用Vector,或者使用Collections的静态方法将ArrayList包装成一个线程安全的类,但是这些方式都是 不念不忘少年蓝@/ 2022年12月28日 08:22/ 0 赞/ 183 阅读
相关 01-Spring底层核心原理解析 Bean查找先根据Bean的类型去(spring容器中-(map))查找,若类型查询不到再根据类型的名称去查找 名称重复会覆盖 --------------- 野性酷女/ 2022年09月10日 08:19/ 0 赞/ 278 阅读
相关 Java HashMap 1.8 底层原理解析 HashMap 原理解析 \ transient:关键字,不去参加序列化操作; 1. HashMap 用于存储数据的,想到底层数据的存储方式,存储数据需要有使 左手的ㄟ右手/ 2022年05月22日 00:12/ 0 赞/ 309 阅读
相关 一文看懂mybatis底层运行原理解析 友情提示 : 本文的重点是解析mybatis执行一次sql 的流程 ,过程中思维比较跳跃,觉得比较难得可以往下查看总结中源码流程 -------------------- 电玩女神/ 2022年02月26日 04:56/ 0 赞/ 424 阅读
还没有评论,来说两句吧...