从源码看 CopyOnWriteArrayList 的线程安全机制
目录一.CpoyOnWriteArrayList 原理简单概述二. CopyOnWriteArrayList 源码分析2.1 属性构造器解读2.2 get 方法分析2.3 add 方法分析2.4 set 方法分析2.5 remove 方法分析三. 总结概括使用过 ArrayList 集合的同学应该大致都知道ArrayList 是一个非线程安全的集合同样Java也为我们提供了线程安全的 List 集合它就是我们本篇文章要说的 CopyOnWriteArrayList。只是因为开发过程中通常会使用其他的的方式保证线程安全所以它使用的频率没有 ArrayList 那么频繁。一.CpoyOnWriteArrayList 原理简单概述CopyOnWriteArrayList 的底层采用了加锁的方式保证线程安全并且加的是 Lock 锁而不是 Sychonized 锁。假如现在有两个线程一个读线程A一个写线程B同时想要想数组中添加元素读线A程就会读取当前内存中 CopyOnWriteArrayList 集合写线程B则是会将内存中的 CopyOnWriteArrayList 集合对象复制一份新的在新复制的集合中执行添加操作添加操作完成之后再将新的集合赋值给原来老的集合并且这个过程中写线程B会获取唯一的 Lock 锁其它写线程会阻塞等待实现读写分离。那么假如说有第三个写线程C也想要执行写数据操作就需要等待写线程B操作完成之后释放 Lock 锁自己获取到 Lock 锁之后才能去执行写入操作。二. CopyOnWriteArrayList 源码分析2.1 属性构造器解读下面是我粘贴的一部分属性getset方法构造方法。1可以看到在 CopyOnWriteArrayList 内部它定义了一个 Lock 锁对象2底层定义了一个名为 array 的对象数组3无参构造可以看出调用无参构造会将 array 数组对象的长度设置为 0只有在进行存储元素的时候才回去扩容public class CopyOnWriteArrayListE implements ListE, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID 8673264195747942595L; /** The lock protecting all mutators */ final transient ReentrantLock lock new ReentrantLock(); /** The array, accessed only via getArray/setArray. */ private transient volatile Object[] array; /** * Gets the array. Non-private so as to also be accessible * from CopyOnWriteArraySet class. */ final Object[] getArray() { return array; } /** * Sets the array. */ final void setArray(Object[] a) { array a; } /** * Creates an empty list. */ public CopyOnWriteArrayList() { setArray(new Object[0]); }2.2 get 方法分析下面是 CopyOnWriteArrayList 的 get 获取元素的方法这里它 index 和数组的长度大小都没有做判断所以很有可能会出现索引越界异常get 获取元素的方法很简单没有出现加锁的行为public E get(int index) { // 直接返回对象 index 位置的元素 return get(getArray(), index); }2.3 add 方法分析public void add(int index, E element) { // 获取 Lock 锁 final ReentrantLock lock this.lock; // 调用方法上锁 lock.lock(); try { // 获取内存的数组对象并赋值为 elements Object[] elements getArray(); // 定义一个变量 len 获取数组的长度 int len elements.length; // 判断方法的参数 index 是否越界或合法 if (index len || index 0) throw new IndexOutOfBoundsException(Index: index , Size: len); // 定义一个新数组对象 newElements Object[] newElements; // 定义一个变量 numMoved 接收数组长度 - index 的值 int numMoved len - index; // 如果 numMoved 为0则说明要把新添加的元素放在数组的最后 if (numMoved 0) // 调用调用 copyOf 方法将原来数组中的数据全部复制到 newElements中 // 并在数组末尾添加上新的元素 newElements Arrays.copyOf(elements, len 1); else { // 如果 numMoved 不为0则说明要将该元素添加在数组中间的某个位置 // 先将新数组的长度 1 newElements new Object[len 1]; // 将老数组 0~index 之间的数据全部复制到新数组中 System.arraycopy(elements, 0, newElements, 0, index); // 再将 index~数组最后的数据全部复制到新数组中 System.arraycopy(elements, index, newElements, index 1, numMoved); } // 将要添加的元素 element 添加到新数组的 index 的位置 newElements[index] element; // 将老数组地址值赋值给新数组对象 setArray(newElements); } finally { // 操作完毕最后释放锁 lock.unlock(); } }2.4 set 方法分析public E set(int index, E element) { // 获取 Lock 锁 final ReentrantLock lock this.lock; // 调用方法上锁 lock.lock(); try { // 获取内存的数组对象并赋值给一个新的数组对象 elements Object[] elements getArray(); // 获取 index 处的元素 E oldValue get(elements, index); // 判断 oldValue 和要插入的元素是否相等 if (oldValue ! element) { // 获取数组的长度 int len elements.length; // 将原本的数组数据复制到新数组 newElements 中 Object[] newElements Arrays.copyOf(elements, len); // 将 element 放置到新数组的 index 处 newElements[index] element; // 将新数组覆盖原来的数组 setArray(newElements); } else { // Not quite a no-op; ensures volatile write semantics // 进入 else 说明要set的元素在数组中已经存在直接返回原数组 setArray(elements); } // 返回位置 index 处的老的元素 return oldValue; } finally { // 操作完成释放 lock 锁 lock.unlock(); } }2.5 remove 方法分析public E remove(int index) { final ReentrantLock lock this.lock; lock.lock(); try { Object[] elements getArray(); // 获取数组长度 int len elements.length; // 获取 index 处的元素 E oldValue get(elements, index); // 定义 numMoved 计算出要移动的元素的数量 int numMoved len - index - 1; // 如果 numMoved 为0说明要删除的元素恰好是数组的最后一个元素 if (numMoved 0) // 覆盖原来的数组 setArray(Arrays.copyOf(elements, len - 1)); else // numMoved 不为0则定义一个新数组长度为原来的数组长度-1 Object[] newElements new Object[len - 1]; // 将 0~index 处的元素复制到新数组中去 System.arraycopy(elements, 0, newElements, 0, index); // 将 index1~数组最后的元素移动到新数组中 System.arraycopy(elements, index 1, newElements, index, numMoved); // 覆盖原来的数组 setArray(newElements); } // 返回删除的元素值 return oldValue; } finally { // 操作完毕释放 lock 锁 lock.unlock(); } }三. 总结概括经过上面对 add 添加方法get 获取方法set 修改方法remove 删除方法的分析其实也可以看出相比于原来 ArrayList 集合的基础上添加了一把 lock 。在做增改删三种操作的时候搭配上 copy 复制数组的思想就可以做到线程安全这就是 CopyOnWriteArrayList 线程安全的核心设计思想不算特别难理解。

相关新闻

按键中断,input上报demo

按键中断,input上报demo

文章目录前言代码前言 通过gpio的物理按键中断&#xff0c;上报一个input事件 代码 #include <linux/module.h> #include <linux/errno.h> #include <linux/miscdevice.h> #include <linux/kernel.h> #include <linux/major.h> #include <l…

2026/10/19 19:09:20 阅读更多 →
PCIe寄存器深度解析:从硬件能力到驱动调试的实战指南

PCIe寄存器深度解析:从硬件能力到驱动调试的实战指南

1. 项目概述与核心价值搞硬件驱动或者嵌入式系统开发&#xff0c;尤其是涉及到高速总线通信的&#xff0c;PCIe&#xff08;Peripheral Component Interconnect Express&#xff09;绝对是一个绕不开的坎。它早已不是PC主板上那个插显卡的插槽那么简单&#xff0c;在服务器、存…

2026/10/21 1:56:16 阅读更多 →
GitHub热门项目分析方法与技术趋势解读

GitHub热门项目分析方法与技术趋势解读

1. GitHub 热门项目解析方法论作为开发者日常获取技术灵感和工具的重要渠道&#xff0c;GitHub Trending 榜单每周都会涌现出值得关注的新项目。要系统性地分析这些热门项目&#xff0c;我总结了一套行之有效的四步分析法&#xff1a;1.1 数据采集与清洗首先通过 GitHub API 获…

2026/10/19 7:59:34 阅读更多 →

最新新闻

SSM+Vue教学资源库系统开发实战:从上传审核到在线预览

SSM+Vue教学资源库系统开发实战:从上传审核到在线预览

1. 起因&#xff1a;教学资源乱成一锅粥&#xff0c;才决定动手做这个库先交代一下背景。我们这边是搞创客教育的团队&#xff0c;课程横跨图形化编程、开源硬件、3D建模几个方向&#xff0c;老师和助教加起来十几号人&#xff0c;积累的教案、课件、代码示例、电路图、视频素材…

2026/10/21 1:55:36 阅读更多 →
使用 TypeScript 集成 Azure App Configuration:集中配置、Feature Flags 与动态刷新的完整实战指南

使用 TypeScript 集成 Azure App Configuration:集中配置、Feature Flags 与动态刷新的完整实战指南

AI 技能AI 插件人工智能 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,600 agentic skills. Includes CLI, local MCP, c…

2026/10/21 1:55:36 阅读更多 →
戴森球计划工厂蓝图免费仓库:7000+张蓝图放下就能跑

戴森球计划工厂蓝图免费仓库:7000+张蓝图放下就能跑

戴森球计划工厂蓝图免费仓库&#xff1a;7000张蓝图放下就能跑 【免费下载链接】FactoryBluePrints 游戏戴森球计划的**工厂**蓝图仓库 项目地址: https://gitcode.com/GitHub_Trending/fa/FactoryBluePrints 玩《戴森球计划》&#xff0c;多数人都会卡死在开局那半小时…

2026/10/21 1:55:36 阅读更多 →
如何在未越狱 iPhone 上玩 Windows PC 游戏:Madeira iOS 模拟器完整教程

如何在未越狱 iPhone 上玩 Windows PC 游戏:Madeira iOS 模拟器完整教程

如何在未越狱 iPhone 上玩 Windows PC 游戏&#xff1a;Madeira iOS 模拟器完整教程 【免费下载链接】Madeira Run x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT 项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira Madeira 是一款跑在未越…

2026/10/21 1:55:36 阅读更多 →
wifit3 WPA 握手嗅探:5 个蹲守窗口,几分钟抓下 4-way

wifit3 WPA 握手嗅探:5 个蹲守窗口,几分钟抓下 4-way

wifit3 WPA 握手嗅探&#xff1a;5 个蹲守窗口,几分钟抓下 4-way 【免费下载链接】wifit3 Wifite but USB-only & cross-platform. 项目地址: https://gitcode.com/GitHub_Trending/wi/wifit3 wifit3 是一款 USB 网卡审计工具,WPA 握手嗅探是它的默认常驻状态。蹲一…

2026/10/21 1:55:36 阅读更多 →
Docker网络完全指南:从命名空间、网桥到Overlay跨主机通信

Docker网络完全指南:从命名空间、网桥到Overlay跨主机通信

1. 为什么网络是容器化应用最容易翻车的地方先从一个非常典型的场景说起。某团队开发了一个微服务Demo&#xff0c;容器里跑着前端、后端、数据库三个进程&#xff0c;本地开发时一切正常。结果一到部署环境&#xff0c;前端服务就是连不上后端接口&#xff0c;后端日志里全是连…

2026/10/21 1:54:35 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →