CopyOnWriteArrayList源码解析

CopyOnWriteArrayList源码解析

文章目录

  • CopyOnWriteArrayList源码解析
    • 一、CopyOnWriteArrayList
    • 二、总结

一、CopyOnWriteArrayList

JUC 中,对于 ArrayList 的线程安全用法,比较推崇于使用 CopyOnWriteArrayList ,那么CopyOnWriteArrayList是怎么解决线程安全问题的呢,本文通过解析 CopyOnWriteArrayList 的源码,主要对几个常用的函数进行讲解。

在进行 CopyOnWriteArrayList 的源码讲解之前,先看下同样实现了线程安全的 Vector ,之所以不推荐使用 Vector ,主要是性能太差了,可以看下 Vectoradd()get() 的源码:
在这里插入图片描述
在这里插入图片描述

可以看到Vector 的添加和读取操作都被加上了 synchronized 锁,当并发情况下,因为锁的存在相当于变成了单线程的操作,所以效率肯定低,同样这样的优点就是保证了数据的唯一性,不会读取到脏数据。

下面再看下 CopyOnWriteArrayList 是如何解决并发问题的呢。

首先看下 CopyOnWriteArrayList 的全局变量有哪些:
在这里插入图片描述

其中 lock 锁就是每次在做写操作时,锁的句柄,array 就是具体存储数据的数组,注意这里的 arrayvolatile 所修饰,因此可以在并发情况下实现数据的可见性。

new 创建了一个 CopyOnWriteArrayList 时,如果是使用无参的构造函数,则将 array 的长度默认成 0 ,创建了一个空的数组。

在这里插入图片描述在这里插入图片描述

在使用 add() 添加数据时,先使用 lock.lock() 上锁,并获取到当前的 array 数组,然后对 array 进行 copyOf(),新的数组的长度是之前的长度 +1 ,这样才能存放当前新的值,将新值填充后,再替换掉旧的 array 数组后,释放当前锁。
在这里插入图片描述

在使用 get() 获取指定下边数据时,直接对当前的 array 进行操作:

在这里插入图片描述

在进行 remove() 删除时,先使用 lock.lock() 上锁,然后再获取当前的 array 数组,如果传入的 index 正好是最后一个,那么 numMoved 计算出来就是 0 ,则使用 copyOf() ,长度进行 -1 去除最后一个数据。否则传入的不是最后一个,先声明一个新的 array 数组,数组的长度就是旧的 arraylen - 1,再将 0index 的数据 arraycopy() 至新的 array 数组,然后再将 index + 1 后的再 arraycopy() 至新的 array 数组,最后将新的 array 数组替换旧的,然后释放锁。

在这里插入图片描述

二、总结

  1. new 新建一个 CopyOnWriteArrayList 后会生成一个数组 array 来存放添加的内容,如果是无参的构造函数,则 array 的长度为 0 ,添加数据时再进行扩容。同时会声明一个 ReentrantLock锁。
  2. 当进行 add() 操作时,先进行上锁,然后对当前的 array 进行 copyOf(),并且新的长度是之前的长度 +1 ,这样才能存放当前新的值,将新值填充后,再替换掉旧的 array 数组后,释放当前锁。
  3. 当使用 get() 获取数据时,无需上锁,直接读取当前 array 数组的指定位置。
  4. 当使用 remove() 时,同样先进行上锁,然后再获取当前的 array 数组,如果传入的 index 正好是最后一个,则使用 copyOf() ,长度进行 -1 ,否则的话先声明一个新的 array 数组,现将 0index 的数据 arraycopy() 至新的 array 数组,然后再将 index + 1 后的再 arraycopy() 至新的 array 数组,最后将新的 array 数组替换旧的,然后释放锁。
    读下来之后可以感觉出来 CopyOnWriteArrayList 的源码非常容易理解和阅读,同时也可以看出来,CopyOnWriteArrayList 实现了写写隔离,但读读是可以共享的,这就有可能出现当某个数据再修改时,读进行了操作,导致读取到的还是旧的数据。还有就是每次写操作都对数组进行 Copy ,假如数据量非常大的情况下,进行 Copy 消耗的资源则会进行 x 2 ,因此使用 CopyOnWriteArrayList 时,需要考虑下自己的数据量以及读写的频次。

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

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

相关文章

前端 | iframe框架标签应用

文章目录 📚嵌入方式📚图表加载显示📚100%嵌入及滑动条问题📚加载动画保留 前情提要: 计划用iframe把画好的home1.html(echarts各种图表组成的html数据大屏)嵌入整合到index.html(搭…

API接口测试工具的主要作用及选择指南

API接口测试是现代软件开发中至关重要的一环。为了确保不同组件之间的无缝集成和功能正常运作,API接口测试工具应运而生。本文将介绍API接口测试工具的主要作用,以及在选择适合项目的工具时需要考虑的因素。 1、功能测试:API接口测试工具的首…

多线程访问资源计数不正确问题分析

线程1:首先修改状态为-1,然后分配资源,资源分配成功后,修改状态为0 线程2:查询状态为-1,然后分配资源,资源分配成功后,修改状态为0 存在这种情况,在线程1修改状态为-1时&…

云服务器哪家便宜?亚马逊AWS等免费云服务器推荐

在这数字化的时代,云计算技术越来越广泛应用于各种场景,尤其是云服务器,作为一种全新的服务器架构正在逐渐取代传统的物理服务器,“云服务器哪家便宜”等用户相关问题也受到越来越多的关注。自从亚马逊最早推出了首个云计算服务—…

RF Power Generator射频源维修射频匹配器维修

RF MATCH射频匹配器维修范围有ADVANCED射频电源匹配器;ASTEX射频电源匹配器;NP射频电源匹配器;ASTECH射频电源匹配器;SEREN射频电源匹配器;射频电源匹配器;KYOSAN射频电源匹配器;ENI射频电源匹配…

5.3 Windows驱动开发:内核取应用层模块基址

在上一篇文章《内核取ntoskrnl模块基地址》中我们通过调用内核API函数获取到了内核进程ntoskrnl.exe的基址,当在某些场景中,我们不仅需要得到内核的基地址,也需要得到特定进程内某个模块的基地址,显然上篇文章中的方法是做不到的&…

【Java Spring】SpringBoot常用插件

文章目录 1、Lombok1.1 IDEA社区版安装Lombok1.2 IDEA专业版安装Lombok1.3 Lombok的基本使用 2、EditStarters2.1 IDEA安装EditStarters2.2 EditStarters基本使用方法 1、Lombok 是简化Java开发的一个必要工具,lombok的原理是编译过程中将lombok的注解给去掉并翻译…

基于SpringBoot的公益慈善平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取项目下载方式🍅 一、项目背景介绍: 基于SpringBoot的公益…

sqli-labs靶场详解(less29-less31)

less-29 提示有最好的防火墙 小白原因 这种题型没见过 先自己测试一下 ?id1 to use near 1 预计可以使用报错注入 和单引号有关的注入点 ?id1 and 11 成功 ?id1 and 12 失败 确实是单引号字符型注入点 ?id1;%00 id1%27;%00 获取到了%00空字符(原因就是服务器获取…

手把手教你:基于python+Django的英文数据分析与可视化系统

系列文章 手把手教你:基于Django的新闻文本分类可视化系统(文本分类由bert实现)手把手教你:基于python的文本分类(sklearn-决策树和随机森林实现)手把手教你:基于TensorFlow的语音识别系统 目录…

LeetCode-面试题08.01 三步问题 C/C++实现 超详细思路及过程[E]

🎈归属专栏:深夜咖啡配算法 🚗个人主页:Jammingpro 🐟记录一句:摆了一个周末了,不能摆了,努力起来!! 文章目录 LeetCode-面试题08.01 三步问题🚗题…

大语言模型(LLMs)在 Amazon SageMaker 上的动手实践(一)

本期文章,我们将通过三个动手实验从浅到深地解读和演示大语言模型(LLMs),如何结合 Amazon SageMaker 的模型部署、模型编译优化、模型分布式训练等。 实验一:使用 Amazon SageMaker 构建基于开源 GPT-J 模型的对话机器…