从源码看 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/7/22 15:52:11 阅读更多 →
PCIe寄存器深度解析:从硬件能力到驱动调试的实战指南

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

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

2026/7/23 17:36:15 阅读更多 →
GitHub热门项目分析方法与技术趋势解读

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

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

2026/7/22 15:52:11 阅读更多 →

最新新闻

Tiva™ TM4C129LNCZAD GPTM定时器中断配置与寄存器详解

Tiva™ TM4C129LNCZAD GPTM定时器中断配置与寄存器详解

1. GPTM中断与定时器配置的核心逻辑在嵌入式开发里&#xff0c;定时器就像你手腕上的秒表&#xff0c;而中断就是秒表到点后发出的“滴滴”声&#xff0c;提醒你该干下一件事了。Tiva™ TM4C129LNCZAD微控制器里的通用定时器模块&#xff08;GPTM&#xff09;功能强大&#xff…

2026/7/23 17:36:16 阅读更多 →
【毕业设计】轻量化物资配送流程管理系统的设计与实现 基于 Django 的仓储物资配送跟踪管理系统(源码+文档+远程调试,全bao定制等)

【毕业设计】轻量化物资配送流程管理系统的设计与实现 基于 Django 的仓储物资配送跟踪管理系统(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

2026/7/23 17:36:16 阅读更多 →
OpenCode 流览器控制实现企业批量信息提取(Selenium 复用 Chrome 登录态)

OpenCode 流览器控制实现企业批量信息提取(Selenium 复用 Chrome 登录态)

一、前言做尽调、股权穿透、企业背景分析时&#xff0c;经常需要批量导出目标公司全部自然人股东&#xff0c;再逐一抓取每位股东名下所有任职企业。手动点开几十上百个股东页面效率极低&#xff0c;本文分享一套基于 SeleniumChrome 调试端口复用登录态的自动化采集方案&#…

2026/7/23 17:36:16 阅读更多 →
【毕业设计】基于 Django 的电商交易平台及其数据安全机制实现(源码+文档+远程调试,全bao定制等)

【毕业设计】基于 Django 的电商交易平台及其数据安全机制实现(源码+文档+远程调试,全bao定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

2026/7/23 17:36:16 阅读更多 →
ARM Cortex-M定时器与看门狗寄存器级精解:从GPTMTAV到WDTLOAD的实战指南

ARM Cortex-M定时器与看门狗寄存器级精解:从GPTMTAV到WDTLOAD的实战指南

1. 项目概述与核心价值在嵌入式开发领域&#xff0c;尤其是基于ARM Cortex-M内核的微控制器项目里&#xff0c;通用定时器&#xff08;GPTM&#xff09;和看门狗定时器&#xff08;WDT&#xff09;是两个你必须打交道的核心外设。它们一个负责精准的“时间管理”&#xff0c;另…

2026/7/23 17:36:16 阅读更多 →
记一次懒人在openwrt软路由系统下更改alias,并永久生效的方法

记一次懒人在openwrt软路由系统下更改alias,并永久生效的方法

由于Esxi openwrt-lede软路由系统不稳定&#xff0c;每次开机都得用xshell连接openwrt-lede软路由系统,并敲下reboot让软路由系统重启&#xff0c;网络才能正常使用&#xff0c;如果更新软路由固件又会导致一些软件功能不可用&#xff0c;故只能硬着头皮每天敲下reboot&#xf…

2026/7/23 17:35:15 阅读更多 →

日新闻

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;从单点好评到指数级传播&#xff1a;AI副业主理人必须掌握的4层口碑渗透模型&#xff08;含ROI测算表&#xff09; 当AI副业主理人不再仅满足于单次服务交付&#xff0c;而是主动构建可复用、可裂变、可…

2026/7/23 0:00:25 阅读更多 →
AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

AI写作开头钩子设计:为什么你的AI文案完读率不足18%?——基于2,346篇A/B测试报告的归因分析

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;AI写作开头钩子设计&#xff1a;为什么你的AI文案完读率不足18%&#xff1f;——基于2,346篇A/B测试报告的归因分析 在对2,346篇跨行业AI生成文案的A/B测试数据进行聚类分析后&#xff0c;我们发现&#xff1…

2026/7/23 0:01:26 阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南&#xff1a;免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:01:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中&#xff0c;我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源&#xff0c;还是配置文件、证书等&#xff0c;都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下&#xff0c;但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP&#xff08;轻量级目录访问协议&#xff09;作为企业级身份认证的黄金标准&#xff0c;已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时&#xff0c;发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 19:43:43 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”&#xff0c;而是以可解释、可审计、可迭代的方式&#xff0c;赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻