并发集合(二):CopyOnWriteArrayList

1、CopyOnWriteArrayList介绍

      CopyOnWriteArrayList 是一个线程安全的ArrayList。
      CopyOnWriteArrayList 是基于Lock锁和线程副本的形式来保证线程安全的,
      在写数据时,先获取Lock锁,然后复制一个副本,添加数据时,是往副本数组中添加数据,最        后再将副本数组赋值给自身array属性,这样可以保证读写分离(读写完全独立),从而提高          读写性能
      CopyOnWriteArrayList 是弱一致性的,写操作先操作,但是副本还没落CopyOnWriteArrayList        的 array属性中,此时读操作是无法读取刚写入的数据的。 

       注意:
              CopyOnWriteArrayList 每次写操作都要复制一个副本,若业务场景是写多多少,并且数                  据量比较大时,则尽量避免使用 CopyOnWriteArrayList;因为这里会创建大量的数组副                  本,比较占用内存资源;数组数据量很大时,数组的复制也是很消耗内存资源的

2、核心属性&方法

      CopyOnWriteArrayList 比较简单,只看下2个属性,2个设置array属性的方法及1个无参构造

      函数就行了,如下所示:

//lock锁,写操作时先获取锁
final transient ReentrantLock lock = new ReentrantLock();
//真正保存数据的数组
private transient volatile Object[] array;/*** 获取array属性*/final Object[] getArray() {return array;}/*** 替换array属性*/final void setArray(Object[] a) {array = a;}public CopyOnWriteArrayList() {//默认数组长度为0//因为每次写数据时,都会构建一个全新的数组,所以在 CopyOnWriteArrayList 中也没有数组扩容的操作setArray(new Object[0]);}

3、读操作

     CopyOnWriteArrayList 读操作就是执行get方法,通过数组的索引位置来获取数据;

     具体方法如下:

            

4、写操作

     CopyOnWriteArrayList 写操作是基于Lock 锁+数组副本来保证写操作的线程安全的;

      且与读操作隔离,相互独立,互不影响。

      写操作方法如下:

      4.1)add(E e) 方法

               默认在副本数组的末尾添加数据

   /*** 添加数据,不指定索引位置,默认是在最后边添加数据*/public boolean add(E e) {final ReentrantLock lock = this.lock;//获取锁lock.lock();try {//构建array属性的副本Object[] elements = getArray();int len = elements.length;//创建新的数组,并将副本elements 的数据赋值到 newElements 中Object[] newElements = Arrays.copyOf(elements, len + 1);//添加数据,在最后添加newElements[len] = e;//将 newElements 赋值给array属性setArray(newElements);return true;} finally {//释放锁lock.unlock();}}

      4.2)add(E e,int index) 方法

               在指定索引位置index处添加数组,但不会覆盖索引index原有的数据

   /*** 在指定位置index处添加数据,但不会覆盖数据*/public void add(int index, E element) {final ReentrantLock lock = this.lock;//加锁lock.lock();try {//获取array属性的值,即复制 array 副本Object[] elements = getArray();int len = elements.length;//判断索引位置是否合法 0<= index <= lenif (index > len || index < 0)throw new IndexOutOfBoundsException("Index: "+index+", Size: "+len);Object[] newElements;int numMoved = len - index;if (numMoved == 0) //表示新数据需要放到当前数组的最后边newElements = Arrays.copyOf(elements, len + 1);else {//表示当前数据需要放到索引在[0,len-1] 的范围内//创建新的数组,长度是array属性长度加1newElements = new Object[len + 1];//将老数组elements的[0,index) 位置的数据复制到新数组 newElements 的[0,index)处System.arraycopy(elements, 0, newElements, 0, index);//将老数组elements的[index,len) 位置的数据复制到新数组 newElements 的[index+1,len]处System.arraycopy(elements, index, newElements, index + 1,numMoved);}//数据正常放到索引位置index处newElements[index] = element;setArray(newElements);} finally {//释放锁lock.unlock();}}

5、移除数据

     CopyOnWriteArrayList 移除数据有2种情况,一是根据索引index删除,二是删除指定数据,若

     数据不存在,则返回null

     5.1、remove(int index):据索引index删除指定位置的数据

              

public E remove(int index) {final ReentrantLock lock = this.lock;//加锁lock.lock();try {//获取array属性值Object[] elements = getArray();int len = elements.length;//获取指定索引位置index处的值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];//删除数据//将旧数组 elements 的[0,index)处的数据复制到新数组 newElements 的[0,index)处System.arraycopy(elements, 0, newElements, 0, index);//将旧数组 elements 的[index+1,len)处的数据复制到新数组 newElements 的[index,len-1)处System.arraycopy(elements, index + 1, newElements, index,numMoved);//设置array属性setArray(newElements);}//返回删除的数据return oldValue;} finally {lock.unlock();}}

     5.2、remove(Object o):删除指定的数据

public boolean remove(Object o) {//获取array的副本Object[] snapshot = getArray();//获取要删除数据的位置索引,若数据不存在,则返回-1//todo 注意:若是并发场景下,这里获取到的数据o的位置index不一定正确int index = indexOf(o, snapshot, 0, snapshot.length);return (index < 0) ? false : remove(o, snapshot, index);}private boolean remove(Object o, Object[] snapshot, int index) {final ReentrantLock lock = this.lock;//加锁lock.lock();try {//新获取array的副本Object[] current = getArray();int len = current.length;// findIndex 是if 代码块的名称// snapshot != current 表示并发下array已经被其他线程修改了,// 那么参数传递来的删除数据o的位置index也不正确,需要重新获取indexif (snapshot != current) findIndex: {//若 index>len :则说明array被其他线程删除了index之后的数据//index<= len: 则表示array可能新增、删除了数据int prefix = Math.min(index, len);for (int i = 0; i < prefix; i++) {//current[i] != snapshot[i]:表示数组array在并发下发生了改变//eq(o, current[i]):判断当前i位置的数据与要删除的数据o是否一致,若一致,则将i赋值给indexif (current[i] != snapshot[i] && eq(o, current[i])) {index = i;//退出if块break findIndex;}}//如果for循环结束还没找到要删除的数据,则表示数组array发生了改变//删除数据不存在if (index >= len)return false;//判断当前index位置的数据是否是要删除的数据,若是,则跳出if 代码块 findIndexif (current[index] == o)break findIndex;//重新查找删除数据o的索引位置index = indexOf(o, current, index, len);//没找到if (index < 0)return false;}//删除数据Object[] newElements = new Object[len - 1];System.arraycopy(current, 0, newElements, 0, index);System.arraycopy(current, index + 1,newElements, index,len - index - 1);//设置array属性setArray(newElements);return true;} finally {//释放锁lock.unlock();}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/415541.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt 按钮图片文字上下结构(纯qss实现)

效果图 实现 QSS QPushButton {border-radius: 6px;opacity: 1;font: 9pt "微软雅黑";color: #333;padding-top:20px;background: url(:/images/ico_un_collected.png) top center no-repeat; } QPushButton:hover {color: #408cff; } QPushButton:pressed {color: …

14、Django Admin的“Action(动作)”中添加额外操作

如图红框增加操作 将以下代码添加到HeroAdmin类中 actions ["mark_immortal"] def mark_immortal(self, request, queryset):queryset.update(is_immortalTrue) 修改后完整代码如下&#xff1a; admin.register(Hero) class HeroAdmin(admin.ModelAdmin):list_di…

固定式测斜仪在铁路防滑坡中的应用

在铁路建设与运营过程中&#xff0c;滑坡作为一种严重的地质灾害&#xff0c;不仅威胁着工程安全&#xff0c;还直接关系到人民生命财产的安全。面对滑坡频发&#xff0c;特别是南方铁路干线所处的地质灾害易发区域&#xff0c;如何有效识别、监测并预防滑坡&#xff0c;成为铁…

【C++从练气到飞升】19---哈希:哈希冲突 | 哈希函数 | 闭散列 | 开散列

&#x1f388;个人主页&#xff1a;库库的里昂 ✨收录专栏&#xff1a;C从练气到飞升 &#x1f389;鸟欲高飞先振翅&#xff0c;人求上进先读书&#x1f389; 目录 ⛳️推荐 一、unordered 系列关联式容器 二、unordered_map 1.1 unordered_map 介绍 1.2 unordered_map 的…

NoSql数据库 - Redis Cluster集群详解及案例实现

Redis Cluster集群&#xff08;无中心化设计&#xff09; 1.1 Redis Cluster 工作原理 在哨兵sentinel机制中&#xff0c;可以解决redis高可用问题&#xff0c;即当master故障后可以自动将slave提升为master&#xff0c;从而可以保证redis服务的正常使用&#xff0c;但是无法…

HIOKI功率分析仪PW3390-03

HIOKI功率分析仪PW3390-03 实现高级别的功率基本精度0.04%rdg.0.05%f.s •200kHz的测量频带&#xff0c;高频也很稳定的振幅和相位精度 •实现机身小型/轻巧化&#xff0c;满足现场和研究室的高精度测量 •50ms高精度高速运算过渡状态的功率&#xff0c;同时并行运算谐波分析、…

Spring优缺点和SpringBoot基础和搭建

前言 Spring框架是一个流行的Java企业级开发框架&#xff0c;旨在简化应用程序开发。它的核心特性包括依赖注入和面向切面编程&#xff0c;提供了灵活性和强大的社区支持。然而&#xff0c;Spring也存在学习曲线陡峭和配置复杂等缺点。 Spring Boot是基于Spring的项目&#x…

观测云核心技术解密:eBPF Tracing 实现原理

前言 eBPF 是一种强大的内核技术&#xff0c;允许在内核中安全地执行自定义代码。通过 eBPF&#xff0c;开发者可以在不修改内核源码的情况下&#xff0c;对内核功能进行扩展和监控。eBPF Tracing 利用这一技术&#xff0c;对系统调用、内核函数等进行跟踪&#xff0c;从而实现…

OpenCV下的无标定校正(stereoRectifyUncalibrated)

OpenCV下的无标定校正(stereoRectifyUncalibrated) 文章目录 1. 杂话2. 无标定校正2.1 先看代码2.2 一点解释2.3 findFundamentalMat参数2.4 stereoRectifyUncalibrated参数 3. 矫正结果 1. 杂话 咱们在之前的帖子里面讲了一些比较常规的标定和校正OpenCV下的单目标定&#xff…

解决bug: RuntimeError: Address already in use,一个linux下pytorch多卡训练tcp端口占用的bug

时间&#xff1a;2024.9.3 1&#xff09;bug&#xff1a; self._store TCPStore( # type: ignore[call-arg] RuntimeError: Address already in use2&#xff09;原因分析 linux下pytorch多卡训练深度学习模型&#xff0c;训练中途暂停训练&#xff0c;但仍有进程占用某个端…

OcrLiteNcnn:Windows环境打包及Java调用

目录结构 前言cmake安装源码下载说明Windows源码编译执行“cmake -DCMAKE_BUILD_TYPE=Release ..”执行“cmake --build . --config Release -- -m:6”编译完成识别图片命令行调用Java调用前言 Java实现OCR识别图片中的文字,小编先前整理过一篇在Linux环境中基于“ChineseOcr…

iPhone升级iOS 18后遭遇“白苹果”?别怕,这几招教你轻松应对!

在这个科技日新月异的时代&#xff0c;苹果iOS系统的每一次更新都牵动着亿万果粉的心。iOS 18作为苹果最新的操作系统&#xff0c;不仅带来了全新的功能体验&#xff0c;也难免会遇到一些用户反馈的“小插曲”&#xff0c;比如升级后出现的“白苹果”现象。面对这一状况&#x…

安卓13拦截home功能 监听home键 禁用home键

总纲 android13 rom 开发总纲说明 目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 经常遇

KAN学习Day1——模型框架解析及HelloKAN

说明 最近了解到了一个新东西——KAN&#xff0c;我的毕设导师给推荐的船新框架。我看过很多剖析其原理的文章&#xff0c;发现大家对其持有的观点都各不相同&#xff0c;有的说可以颠覆传统MLP&#xff0c;有的说可以和Transformer同等地位&#xff0c;但是也有人说它训练速度…

缓存解决方案。Redis 和 Amazon ElastiCache 比较

欢迎来到雲闪世界。Redis 和 Amazon ElastiCache 等缓存解决方案是通过将频繁访问的数据存储在内存中来提高应用程序性能的热门选择。让我们从实施简单性、性能、成本和维护方面对它们进行比较。 实施简单 设置 Redis 需要在基础设施或云实例上安装和配置 Redis 服务器。它可…

通过cmd命令的方式转码MP4为webp动图。附带命令解释。

zihao 通过cmd命令的方式转码MP4为webp动图&#xff1a; 均衡大小和z效果的配置&#xff08;直接拷贝后需要改下路径&#xff09;&#xff1a; ffmpeg -i E:\steam\222.mp4 -vcodec libwebp -filter:v fpsfps24 -lossless 0 -compression_level 5 -q:v 35 -loop 1 -preset def…

C++系列-STL容器之list

STL容器之list list容器的基本结构list容器的特点list容器的优点list容器的缺点 list容器的构造函数list容器的常用接口list赋值操作list大小及空否list访问list迭代器相关list增删查改push and popinsert其它 寄扬州韩绰判官 杜牧〔唐代〕 青山隐隐水迢迢&#xff0c;秋尽江南…

覃嘉仪,艺人经纪人、经纪人、影视经纪人。2002.7.9出生于四川省遂宁市射洪县

覃嘉仪&#xff0c;艺人经纪人、经纪人、影视经纪人。2002.7.9出生于四川省遂宁市射洪县 2020年开始从事宣传工作&#xff0c;2023成为“WP经纪工作室”艺人经纪&#xff0c;现担任孙亦欣、魏逸熙等艺人的经纪人。 2024年涉足于影视行业&#xff0c;并加入嘉林娱乐。2024年在由…

慢慢写材料

有一次&#xff0c;我要公司帮我提供一个材料。当时&#xff0c;我比较着急&#xff0c;于是非常简要、快速的写了一封邮件&#xff0c;发给公司负责人。 结果&#xff0c;两三天后&#xff0c;也没有收到公司的回复。 于是&#xff0c;我又耐心的写了一封新的邮件。说明&#…

华为云CCE集群创建loadBalancer

目录 一、目的 二、创建应用 三、创建服务 loadBalancer 四、域名解析 五、验证 一、目的 1、为CCE容器应用创建loadBalancer服务&#xff0c;并且绑定https协议的域名 2、公网访问域名: https://test.******.com 3、CCE创建用于公网域名访问的loadBalancer&#xff0c;不…