深入浅出CopyOnWriteArrayList 📅 发布时间:2026/7/12 2:47:14 👁️ 浏览次数: CopyOnWriteArrayList 是线程安全的可以在多线程环境下使用。CopyOnWriteArrayList 遵循写时复制的原则每当对列表进行修改例如添加、删除或更改元素时都会创建列表的一个新副本这个新副本会替换旧的列表而对旧列表的所有读取操作仍然可以继续。由于在修改时创建了新的副本所以读取操作不需要锁定。这使得在多读取者和少写入者的情况下读取操作非常高效。当然由于每次写操作都会创建一个新的数组副本所以会增加存储和时间的开销。如果写操作非常频繁性能会受到影响。什么是 CopyOnWriteReentrantReadWriteLock 读写锁是通过读写分离的思想来实现的即读写锁将读写操作分别加锁从而实现读写操作的并发执行。但是读写锁也存在一些问题比如说在写锁执行后读线程会被阻塞直到写锁被释放后读线程才有机会获取到锁从而读到最新的数据站在读线程的角度来看读线程在任何时候都能获取到最新的数据满足数据实时性。而 CopyOnWriteArrayList 是通过 Copy-On-Write(COW)即写时复制的思想来通过延时更新的策略实现数据的最终一致性并且能够保证读线程间不阻塞。当然这要牺牲数据的实时性。通俗的讲CopyOnWrite 就是当我们往一个容器添加元素的时候不直接往容器中添加而是先复制出一个新的容器然后在新的容器里添加元素添加完之后再将原容器的引用指向新的容器。多个线程在读的时候不需要加锁因为当前容器不会添加任何元素。CopyOnWriteArrayList原理顾名思义实际上 CopyOnWriteArrayList 内部维护的就是一个数组/** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array;该数组被 volatile 修饰能够保证数据的内存可见性。get 方法get 方法的源码如下public E get(int index) { return get(getArray(), index); } /** * Gets the array. Non-private so as to also be accessible * from CopyOnWriteArraySet class. */ final Object[] getArray() { return array; } private E get(Object[] a, int index) { return (E) a[index]; }get 方法的实现非常简单几乎就是一个“单线程”没有添加任何的线程安全控制没有加锁也没有 CAS 操作原因就是所有的读线程只会读取容器中的数据并不会进行修改。add 方法add 方法的源码如下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 方法的逻辑也比较容易理解需要注意这么几点1、采用 ReentrantLock 保证同一时刻只有一个写线程正在进行数组的复制2、通过调用getArray()方法获取旧的数组。final Object[] getArray() { return array; }03、然后创建一个新的数组把旧的数组复制过来然后在新的数组中添加数据再将新的数组赋值给旧的数组引用。final void setArray(Object[] a) { array a; }根据 volatile 的 happens-before 规则所以这个更改对所有线程是立即可见的。04、最后在 finally 块中释放锁以便其他线程可以访问和修改列表。CopyOnWriteArrayList 的使用CopyOnWriteArrayList 的使用非常简单和 ArrayList 的使用几乎一样只是在创建对象的时候需要使用 CopyOnWriteArrayList 的构造方法如下所示CopyOnWriteArrayListString list new CopyOnWriteArrayList(); list.add(element1); list.add(element2); for (String element : list) { System.out.println(element); }CopyOnWriteArrayList 的缺点CopyOnWrite 容器有很多优点但是同时也存在两个问题即内存占用问题和数据一致性问题。所以在开发的时候需要特别注意。内存占用问题因为 CopyOnWrite 的写时复制机制在进行写操作的时候内存里会同时有两个对象旧的对象和新写入的对象。数据一致性问题CopyOnWrite 容器只能保证数据的最终一致性不能保证数据的实时一致性。所以如果你希望写入的的数据马上能读到请不要使用 CopyOnWrite 容器最好通过 ReentrantReadWriteLock 自定义一个的列表。比较一下 CopyOnWrite 和读写锁。相同点两者都是通过读写分离的思想来实现的读线程间是互不阻塞的不同点为了实现数据实时性在写锁被获取后读线程会阻塞或者当读锁被获取后写线程会阻塞从而解决“脏读”的问题。而 CopyOnWrite 对数据的更新是写时复制的因此读线程是延时感知的单不会存在阻塞的情况。
互联网大厂Java面试实录:Spring Boot与微服务在电商场景中的应用 互联网大厂Java面试实录:Spring Boot与微服务在电商场景中的应用 场景背景 在当前电商行业高速发展的背景下,企业对Java技术栈的掌握要求日益提升。本文通过真实模拟互联网大厂Java开发岗位的面试,展示了从核心语言到微服务架构的技术点考察&… 2026/7/12 2:45:10
少走弯路:降AIGC软件,专科生首选千笔AI VS Checkjie 在AI技术迅速发展的今天,越来越多的专科生开始借助AI工具辅助完成论文写作,以提高效率、优化内容结构。然而,随着学术查重系统对AI生成内容的识别能力不断提升,论文中的“AI痕迹”和“重复率超标”问题愈发突出,成为影… 2026/7/9 10:09:53
06实战处理AI音乐技术详解第一阶段:频谱破坏·卓伊凡 06实战处理AI音乐技术详解第一阶段:频谱破坏卓伊凡第一阶段:频谱破坏(Frequency De-Perfect)目标:把 AI 音乐那种“过于干净、过于标准、过于理性”的频谱,拉回现实世界的质感。0. 你先记住这句话AI 音乐的… 2026/7/10 17:05:22
夜灯公考 AI 全链路实测:选岗、答疑、督学、模考四个场景的技术拆解 夜灯公考 2022 年成立,有别于中公、华图的线下大班模式,它的产品逻辑从一开始就是线上小班 AI 全链路 三对一督学。在公考培训行业,大部分机构的 AI 还停留在"智能推题"层面。夜灯把 AI 嵌入了学习的每一个环节。下面逐一拆解。场… 2026/7/12 2:46:13
ENVI 5.x 遥感图像预处理实战:Landsat 8 真彩色合成与 2% 线性拉伸 ENVI 5.x 遥感图像预处理实战:Landsat 8 真彩色合成与 2% 线性拉伸技术解析当第一次在ENVI中打开Landsat 8原始数据时,很多初学者会困惑于为什么图像看起来灰暗模糊——这其实是因为原始数字量化值(DN)未经优化显示处理。本文将深入解析如何通过波段组合… 2026/7/12 2:46:13
Cocos Creator 滑动小方块,一笔画 前几天玩了个微信小游戏,一笔画,玩的挺起劲的,就弄了个Demo弄一个二位数组:const layout1: number[][] [[1, 1, 1, 1], [2, 1, 1, 1], [-1, 1, 1, -1], [1, 1, 1, -1]];1表示有方块-1表示没有方块2表示起始的方块随别啦ÿ… 2026/7/12 2:44:13
为什么你的ChatGPT无法处理截图?ChatGPT多模态权限配置失效的7种隐性原因及3分钟修复方案 更多请点击: https://codechina.net 第一章:ChatGPT 多模态功能介绍 ChatGPT 的多模态能力标志着大语言模型从纯文本交互迈向跨模态理解与生成的新阶段。当前版本支持图像输入理解、文本-图像联合推理,并可基于视觉内容生成精准描述、代码片… 2026/7/12 2:42:12
探寻罗姐寨 这几天高温,在家呆着也觉没意思。周六老公休息,出去避暑。罗姐寨去万仙山曾见过村牌,它在万仙山后面的红岩绝壁上,南边挨着郭亮村。等我们到了村头,看到村牌也改成了石碑型,虽然精致了,但是有点… 2026/7/12 2:40:11
Picsum API v2 实战:5分钟构建前端图片占位符服务(含防缓存策略) Picsum API v2 实战:5分钟构建前端图片占位符服务(含防缓存策略)在当今快节奏的前端开发中,快速获取高质量的占位图片是每个开发者都会遇到的常见需求。无论是原型设计、布局测试还是内容填充,一个稳定可靠的图片占位服… 2026/7/12 2:40:11
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/12 0:01:13
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/12 0:01:13
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/12 0:03:14
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/12 0:01:13
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/12 0:01:13
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/12 0:03:14