解析CopyOnWrite机制 以java的CopyOnWriteArrayList为例

什么是CopyOnWrite

写时复制(Copy-on-write,简称COW)是读写分离的一种实现方式,因为读和写在不同的容器中。

核心思想:线程在修改数据的时,会将原数据复制一份,然后在副本上修改,最后再把原数据的引用(指针)更新为新数据的引用(指针)。

CopyOnwrite特点

  • 可以让读线程和写线程并发执行,读线程可以完全不加锁,能提高效率。(优点)
  • 不能保证读写过程中数据的实时一致性(只能保证弱一致性/最终一致性),因为在写入后,原本读线程读取到的数据不会马上更新(需要等本次get结束后后面的get才能读到最新的数据),只有最新的读取才是最新的数据。(缺点)
  • 需要额外的空间开销,因为每次修改都要copy一份数据(缺点)

注意:

  • 在副本上修改完内容后,并不是直接将新旧的元素值替换为的元素值,而是替换地址(引用);比如原数组int[] Data = array1,而array1={1,2,3},副本数组是array2={1,2,3},把array2修改成{3,4,5}后,直接把array1弃用掉,此时Data = array2 ,值为{3,4,5}
  • 写和写肯定还是不能并发执行的,要加互斥锁

图解CopyOnWrite流程

首先,可以有多个读线程读数据,然后只能有一个写线程修改数据,修改数据时,先copy一个副本,然后在副本上修改

在这里插入图片描述

修改完副本内容之后,把原本引用覆盖掉,此时原本的读线程如果当前读取操作没有执行完毕,则读取的还是原来的数据,而如果来了新的读线程,读取到的就是新的数据(新的内存地址)

在这里插入图片描述
如果原本读线程1、2、3刚刚的读取操作结束了,进行的新的读取,那此时读取的就一定是新的数据了
在这里插入图片描述

为什么说它不能保证数据的实时一致性?

因为如果在某一次读取过程还没执行完毕时(执行一部分),数据被修改了,那么本次读取对修改就会感知不到,只有下一次读取才是最新的数据。本次读取的数据相对于最新数据来说可能存在滞后性(时延),所以说它不能保证实时一致性,因为实时一致性要求就算你某次读取中途被修改,它也应该立刻感知到。

结合CopyOnWriteArrayList源码分析

底层是一个数组array(只能通过get和set方法访问),用volatile修饰(注意修饰的是数组引用,不是数组内容),如果这个引用被更新了,也就是执行了add、set等方法,其它线程会马上感知到这个数组的变化,后面的读取也就是新的值了

在这里插入图片描述

add方法

/*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return {@code true} (as specified by {@link Collection#add})*/
public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();//加锁,保证写线程互斥try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);//copy一个新数组,size为当前length+1newElements[len] = e;//在新数组上添加元素setArray(newElements);//新数组覆盖旧数组(注意覆盖的是地址,不是数组内容)return true;} finally {lock.unlock();//解锁}
}

set也是类似过程,只不过copy的数组的size和原来的一样

    /*** Replaces the element at the specified position in this list with the* specified element.** @throws IndexOutOfBoundsException {@inheritDoc}*/public E set(int index, E element) {final ReentrantLock lock = this.lock;lock.lock();//加锁try {Object[] elements = getArray();E oldValue = get(elements, index);if (oldValue != element) {int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len);newElements[index] = element;setArray(newElements);} else {// Not quite a no-op; ensures volatile write semanticssetArray(elements);//更新数组}return oldValue;} finally {lock.unlock();//解锁}}

可以看到,传入的a是一个数组,直接把a赋值给array代表的是修改引用

    /*** Sets the array.*/final void setArray(Object[] a) {array = a;}

get方法

    @SuppressWarnings("unchecked")private E get(Object[] a, int index) {return (E) a[index];}

分析:

既然volatile可以保证修改后的可见性,那为什么上面的图2中的读线程读取的还是旧数据?

因为读取方法不是一个原子操作,有可能读操作已经进入到旧的内存中去取值了,就算此时把旧的内存地址换成新的内存地址,本次读操作也感知不到了。

比如CopyOnWriteArrayList的get方法,在一次读取中分成两步

1、先读取原数组内存

2、通过下标访问该内存中指定位置的元素

如果已经执行了步骤1,还没有执行步骤2,这时候有一个写线程把数据修改了,那么执行步骤2的时候访问的就还是旧的数组内容。也就是说某一次get读取过程中数据被修改的话,有可能还是读的是旧的数据,只有下一个读取get才是最新的,这就是数据的弱一致性。

那后续读线程1、读线程2、读线程3后续再继续读数据的时候,读取到的是最新数据还是旧的数据?

是新的,因为volatile会保证更新后其它线程可见。

什么时候用

  • 读多写少的时候用,且尽量用批量修改操作,因为每次修改都会copy副本

和其它实现线程安全的手段有什么区别?

  • 用普通锁(synchronized或可重入锁)实现的是:读读互斥、读写互斥、写写互斥
  • 用读写锁实现的是:读读不互斥、读写互斥、写写互斥
  • 用copyonwrite实现的是:读读不不互斥、读写不互斥、写写互斥

参考链接:

java并发:CopyOnWrite机制

简单聊聊copy on write(写时复制)技术

CopyOnWriterArrayList 详解

Java中的copy on write(COW )是什么?

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

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

相关文章

imx.7交叉编译libX11

背景: 还是之前提到的触摸屏无响应问题,通过GDB调试,发现APP并非人为代码卡死,而是卡在官方的libc.so.6中,这个库出现了一些错误。排除自己代码问题,就剩官方版本问题,移植X11库,或…

提高APP安全性的必备加固手段——深度解析代码混淆技术

​ APP 加固方式 iOSAPP 加固是优化 APK 安全性的一种方法,常见的加固方式有混淆代码、加壳、数据加密、动态加载等。下面介绍一下 iOSAPP 加固的具体实现方式。 混淆代码: 使用 ProGuard 工具可以对代码进行混淆,使得反编译出来的代码很难…

前端本地搭建gninx环境

nginx下载地址: https://nginx.org/en/download.html nginx下载后,解压即用,注意解压目录不要含中文 nginx常用命令 查看版本 nginx -v 开启nginx服务 start nginx 重启服务 nginx -s reload 关闭服务 nginx -s stopnginx目录简析

three.js能够实现的3D动画效果大阅兵,有图有真相。

three.js能够实现许多不同类型的3D交互动画,包括但不限于以下几种: 旋转和缩放:可以通过鼠标或触摸手势来旋转和缩放3D模型或场景。 序列动画:可以创建复杂的动画序列,包括移动、旋转、缩放、颜色变化等。 粒子效果&…

使用Flask部署ppocr模型_3

PaddleOCR环境搭建、模型训练、推理、部署全流程(Ubuntu系统)_1_paddle 多进程推理-CSDN博客 PP-Structure 文档分析-CSDN博客 接前两篇继续完成Flask部署 一、使用Flask部署ppocr模型 GET方法用于从服务器获取资源,即客户端向服务器请求数据…

单链表讲解

一.链表的概念以及结构 链表是一种物理结构上不连续,逻辑结构上连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 链表的结构与火车是类似的,一节一节的,数据就像乘客一样在车厢中一样。 与顺序表不同的…

如何使用pytorch进行图像分类

如何使用pytorch进行图像分类https://featurize.cn/notebooks/5a36fa40-490e-4664-bf98-aa5ad7b2fc2f

【软考】UML中的图之用例图

目录 1. 说明2. 建模2.1 说明2.2 语境建模2.3 需求建模 3. 图示4. 组成部分 1. 说明 1.用例图(Use Case Diagram)。2.展现了一组用例、参与者(Actor)以及它们之间的关系。3.用例图通常包括以下的内容:用例、参与者、用…

配置IP地址并验证连通性

1.实验环境 主机 A和主机 B通过一根网线相连,如图6.13所示。 图6.13 实验案例一示意图 2.需求描述 为两台主机配置!P地址,验证P地址是否生效;验证同一网段的两台主机可以互通,不同网段的主机不能直接互通。 3.推荐步骤 为两台…

uni原生导航栏相关设置

动态设置某一项内容 使用场景:不同角色显示不同导航栏或设置不同名称,不同图标 API: uni.setTabBarItem(OBJECT) 属性类型默认值必填说明indexnumber无是tabBar的哪一项,从左边算起(从0开始)textString无否tab上的按…

【Linux】封装一下简单库 理解文件系统

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、封装一下简单库 二、理解一下stdin(0)、stdout(1)、stderr(3) 2.1、为什么要有0、1、2呢? 2.2、特点 2.3、如果我想让2也和1重定向到一个文件…

uni-app 微信小程序设置全局转发给朋友、分享到朋友圈

小程序右上角原生菜单自带的分享按钮,默认不可用 1.创建一个mixin share.js export default {created() {//#ifdef MP-WEIXINwx.showShareMenu({withShareTicket: true,menus: [shareAppMessage, shareTimeline]});//#endif}, }export default {created() {//#ifde…

vs2022断点调试怎么看堆栈帧,找异常位置

打一个断点以后,会出现如图报错 我们要怎么找到报错的语句?鼠标点击->堆栈帧->上一行运行的位置->直到找到错误出错如图所示: 跳转到,我们手写的代码,执行出错的位置

Unity | Shader基础知识(第十二集:颜色混合)

目录 前言 一、日常生活中的常见现象 二、unity自带的一个结构体(表面着色器SurfaceOutputStandard) 三、自己写一个颜色混合的Shader 1.只加基础颜色Albedo 2.加入法线 3.加入光滑度 4.加入金属度 5.加入自发光 四、作者的话 前言 shader里每一…

docker安装nessus服务及使用

Nessus 是目前全世界最多人使用的系统漏洞扫描与分析软件,现在软件服务越来越多,越来越复杂,涉及的数据也更多;因此系统完成后对于系统漏洞的检测并对其进行修改十分有必要,本文介绍通过docker安装nessus服务及简单的使…

设计模式-模板方法模式(TemplateMethod)

1. 概念 模板方法模式是一种行为设计模式,它在一个方法中定义算法的骨架,将一些步骤延迟到子类中实现。 2. 原理结构图 2.1 图 2.2 角色 抽象类(Abstract Class) 定义抽象的基本操作(Primitive Operations&#xff…

从启发式到模型化 京东推荐广告排序机制演化

1、序言:广告排序机制的前世今生 1.1、简介:广告排序机制 在线广告是国内外各大互联网公司的重要收入来源之一,而在线广告与传统广告最大的区别就在于其超大规模的实时竞价环境:数以万计的广告主在一天内可以参与亿级别的流量竞…

解决宝塔的FTP无法使用被动模式

问题:宝塔安装完ftp管理软件之后,无法使用被动模式连接 解决: 提示: 如果还是不行,那么要看看防火墙和安全组有没有放行被动模式的端口,宝塔安装的pure-ftpd软件的被动模式端口默认是39000至400…

Kubernetes 升级不弃 Docker:KubeKey 的丝滑之道

作者:尹珉,KubeSphere Ambaasador&Contributor,KubeSphere 社区用户委员会杭州站站长。 引言 随着 Kubernetes 社区的不断发展,即将迎来 Kubernetes 1.30 版本的迭代。在早先的 1.24 版本中,社区作出一个重要决策…

计算机网络——42攻击和对策

攻击和对策 IDS:入侵检测系统 分组过滤 对TCP/IP头部进行检查不检查会话间的相关性 IDS:intrusion detection system 深入分组检查:检查分组的内容(e.g. 检查分组中的特征串,已知攻击数据库的病毒和攻击串)检查分组间…