当前位置: 首页 > news >正文

详解 Java 中的 CopyOnWriteArrayList

目录

【1】CopyOnWriteArrayList 简介

【2】核心原理

1.底层数据结构

2.写时复制机制

【3】CopyOnWriteArrayList常用方法及实例

1.添加元素方法 add ()

2.获取元素方法 get ()

3.删除元素方法remove()

【4】优缺点分析

【5】适用场景

【6】总结


【1】CopyOnWriteArrayList 简介

        Copy-On-Write,是一种用于集合的并发访问优化策略。它的基本思想是:当我们往一个集合容器中写入元素时(添加、修改、删除),并不会直接在集合容器中写入,而是先将当前集合容器进行Copy,复制出一个新的容器,然后新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器

        这种策略的优点是:实现对CopyOnWrite集合容器写入操作时的线程安全,但同时并不影响进行并发的读取操作。所以CopyOnWrite容器也是一种读写分离的思想。
        从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发集合容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。

        CopyOnWriteArrayList相当于线程安全的ArrayList,内部存储结构采用Object[]数组,线程安全使用ReentrantLock实现,允许多个线程并发读取,但只能有一个线程写入。


【2】核心原理

1.底层数据结构

        CopyOnWriteArrayList 的底层数据结构是一个数组,使用volatile关键字修饰以保证可见性:

2.写时复制机制

CopyOnWriteArrayList 的核心思想是写时复制(Copy-On-Write):

  • 当进行写操作(添加、修改、删除)时,首先复制一份当前的底层数组
  • 在复制的新数组上执行写操作
  • 操作完成后,将底层数组的引用指向新数组

这种机制保证了读操作永远访问的是一个稳定的数组,不需要加锁。


【3】CopyOnWriteArrayList常用方法及实例

1.添加元素方法 add ()

        添加新元素至集合时,会将当前数组Copy复制新数组,并将新元素添加至新数组,最后替换原数组。执行过程中,使用ReentrantLock加锁,保证线程安全,避免多个线程复制数组。

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();}
}

2.获取元素方法 get ()

        根据指定下标,到原数组中读取元素。读取过程中不加锁,允许多个线程并发读取。但是如果读取的时候,有其它线程同时向集合中添加新元素并未结束,get()方法仍然读取到的是旧数据。

public E get(int index) {// 根据指定下标,从原数组中读取元素return get(getArray(), index);
}private E get(Object[] a, int index) {return (E) a[index];
}

3.删除元素方法remove()

        删除指定下标元素。根据指定下标,从原数组中,Copy复制其它元素至新数组,最后替换原数组。

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)// 复制原数组中,除最后一个元素以外的所有元素,至新数组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();}
}

【4】优缺点分析

  • 优点

    • 读操作性能优异:读操作无需加锁,适合读多写少的场景

    • 迭代安全:迭代过程中不会抛出ConcurrentModificationException

    • 线程安全:通过写时复制和锁机制保证线程安全

  • 缺点

    • 内存占用:写操作时需要复制整个数组,可能导致内存占用翻倍

    • 数据一致性:只能保证最终一致性,不能保证实时一致性

    • 写操作性能差:每次写操作都需要复制数组,开销较大


【5】适用场景

CopyOnWriteArrayList 适用于以下场景:

  1. 读多写少的并发场景,如缓存、配置信息管理

  2. 需要避免迭代过程中ConcurrentModificationException的场景

  3. 数据量不大的场景(避免复制大数组带来的性能开销)

不适合的场景:

  1. 写操作频繁的场景

  2. 对数据实时一致性要求高的场景

  3. 存储大量数据的场景


【6】总结

        CopyOnWriteArrayList 通过独特的写时复制机制,在特定场景下提供了高效的并发处理能力。它不是万能的,但是在读多写少的场景下,相比传统的同步列表实现具有明显的优势。


        感谢你花时间读到这里~ 如果你觉得这篇内容对你有帮助,不妨点个赞让更多人看到;如果有任何想法、疑问,或者想分享你的相关经历,欢迎在评论区留言交流,你的每一条互动对我来说都很珍贵~ 我们下次再见啦!😊😊


文章转载自:

http://VMeWvPgu.bhqLj.cn
http://zD9S8ex4.bhqLj.cn
http://fbvIB11X.bhqLj.cn
http://5xvc7D21.bhqLj.cn
http://DtSf9iy8.bhqLj.cn
http://37vskoSb.bhqLj.cn
http://BX5EQnAX.bhqLj.cn
http://eRyVCfFZ.bhqLj.cn
http://iSqD2B8P.bhqLj.cn
http://eH7DVtDf.bhqLj.cn
http://IOxYcWtc.bhqLj.cn
http://Fhkralqy.bhqLj.cn
http://zHoJKX6H.bhqLj.cn
http://eJUKV1Lh.bhqLj.cn
http://BYyom2Ts.bhqLj.cn
http://bt1NOYp7.bhqLj.cn
http://ZBu6kFYm.bhqLj.cn
http://dTz2g1en.bhqLj.cn
http://7SgzJiPW.bhqLj.cn
http://b7s4zmeh.bhqLj.cn
http://Ho4Qs0e4.bhqLj.cn
http://cADTZjGV.bhqLj.cn
http://2pfK2I8k.bhqLj.cn
http://TRQW0BTh.bhqLj.cn
http://fHZyWoCU.bhqLj.cn
http://4psDQlk0.bhqLj.cn
http://OT07xPQo.bhqLj.cn
http://S0z1ufQ3.bhqLj.cn
http://Y0XZ3Typ.bhqLj.cn
http://JxBzZMPs.bhqLj.cn
http://www.dtcms.com/a/371269.html

相关文章:

  • FTL(Flash Translation Layer)
  • C++输出字符串的统一码(Unicode Code)和 ASCII 码
  • 【PCIe EP 设备入门学习专栏 -- 8.1.2 PCIe EP 通路详细介绍】
  • nginx安装部署(备忘)
  • 6.虚拟化历史
  • 疯狂星期四文案网第62天运营日记
  • AI工程师对于AI的突发奇想
  • 模电仿真软件:MultSim14.3下载与安装
  • 心路历程-passwdusermod命令补充
  • 自旋锁/互斥锁 设备树 iic驱动总线 day66 67 68
  • 【尚跑】2025逐日者15KM社区赛西安湖站,74分安全完赛
  • 页面间的导航:`<Link>` 组件和 `useRouter`
  • 视频动作识别-VideoSwin
  • AI 自然语音对话接入客服系统的场景分析及实现
  • 【基础-判断】架构设计时需要考虑“一次开发,多端部署”,这样可以节省跨设备UI开发工作量,同时提升应用部署的伸缩性。
  • [光学原理与应用-428]:非线性光学 - 为什么要改变光的波长/频率,获得特点波长/频率的光?
  • 运筹学——求解线性规划的单纯形法
  • HTML标签之超链接
  • MySQL问题5
  • MyBatis Example模式SQL注入风险
  • C语言数据结构——详细讲解《二叉树与堆的基本概念》
  • 【杂类】I/O
  • import type在模块引入中的作用
  • MySQL入门指南:从安装到工作原理
  • 【基础-判断】一个页面可以存在多个@Entry修饰的组件。
  • MapStruct详解
  • 新的打卡方式
  • GESP 7/8级免CSP-J/S初赛!申请注意事项!今年已过,明年提前关注!
  • esbuild入门
  • 决策树概念与原理