43 - 什么是数据的强、弱一致性?

news2025/1/22 9:05:38

说到一致性,其实在系统的很多地方都存在数据一致性的相关问题。除了在并发编程中保证共享变量数据的一致性之外,还有数据库的 ACID 中的 C(Consistency 一致性)、分布式系统的 CAP 理论中的 C(Consistency 一致性)。下面我们主要讨论的就是“并发编程中共享变量的一致性”。

在并发编程中,Java 是通过共享内存来实现共享变量操作的,所以在多线程编程中就会涉及到数据一致性的问题。

我先通过一个经典的案例来说明下多线程操作共享变量可能出现的问题,假设我们有两个线程(线程 1 和线程 2)分别执行下面的方法,x 是共享变量:

// 代码 1
public class Example {
    int x = 0;
    public void count() {
        x++;                     //1
        System.out.println(x)//2
    }
}

img

如果两个线程同时运行,两个线程的变量的值可能会出现以下三种结果:

img

1、Java 存储模型

2,1 和 1,2 的结果我们很好理解,那为什么会出现以上 1,1 的结果呢?

我们知道,Java 采用共享内存模型来实现多线程之间的信息交换和数据同步。在解释为什么会出现这样的结果之前,我们先通过下图来简单了解下 Java 的内存模型(第 21 讲还会详解),程序在运行时,局部变量将会存放在虚拟机栈中,而共享变量将会被保存在堆内存中。

img

由于局部变量是跟随线程的创建而创建,线程的销毁而销毁,所以存放在栈中,由上图我们可知,Java 栈数据不是所有线程共享的,所以不需要关心其数据的一致性。

共享变量存储在堆内存或方法区中,由上图可知,堆内存和方法区的数据是线程共享的。而堆内存中的共享变量在被不同线程操作时,会被加载到自己的工作内存中,也就是 CPU 中的高速缓存。

CPU 缓存可以分为一级缓存(L1)、二级缓存(L2)和三级缓存(L3),每一级缓存中所储存的全部数据都是下一级缓存的一部分。当 CPU 要读取一个缓存数据时,首先会从一级缓存中查找;如果没有找到,再从二级缓存中查找;如果还是没有找到,就从三级缓存或内存中查找。

如果是单核 CPU 运行多线程,多个线程同时访问进程中的共享数据,CPU 将共享变量加载到高速缓存后,不同线程在访问缓存数据的时候,都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。

如果是多核 CPU 运行多线程,每个核都有一个 L1 缓存,如果多个线程运行在不同的内核上访问共享变量时,每个内核的 L1 缓存将会缓存一份共享变量。

假设线程 A 操作 CPU 从堆内存中获取一个缓存数据,此时堆内存中的缓存数据值为 0,该缓存数据会被加载到 L1 缓存中,在操作后,缓存数据的值变为 1,然后刷新到堆内存中。

在正好刷新到堆内存中之前,又有另外一个线程 B 将堆内存中为 0 的缓存数据加载到了另外一个内核的 L1 缓存中,此时线程 A 将堆内存中的数据刷新到了 1,而线程 B 实际拿到的缓存数据的值为 0。

此时,内核缓存中的数据和堆内存中的数据就不一致了,且线程 B 在刷新缓存到堆内存中的时候也将覆盖线程 A 中修改的数据。这时就产生了数据不一致的问题。

img

了解完内存模型之后,结合以上解释,我们就可以回过头来看看第一段代码中的运行结果是如何产生的了。看到这里,相信你可以理解图中 1,1 的运行结果了。

img

2、重排序

除此之外,在 Java 内存模型中,还存在重排序的问题。请看以下代码:

// 代码 1
public class Example {
    int x = 0;
    boolean flag = false;
    public void writer() {
        x = 1;                //1
        flag = true;          //2
    }
 
    public void reader() {
        if (flag) {           //3
             int r1 = x;      //4
             System.out.println(r1==x)
        }
    }
}

img

如果两个线程同时运行,线程 2 中的变量的值可能会出现以下两种可能:

img

现在一起来看看 r1=1 的运行结果,如下图所示:

img

那 r1=0 又是怎么获取的呢?我们再来看一个时序图:

img

在不影响运算结果的前提下,编译器有可能会改变顺序代码的指令执行顺序,特别是在一些可以优化的场景。

例如,在以下案例中,编译器为了尽可能地减少寄存器的读取、存储次数,会充分复用寄存器的存储值。如果没有进行重排序优化,正常的执行顺序是步骤 1\2\3,而在编译期间进行了重排序优化之后,执行的步骤有可能就变成了步骤 1/3/2 或者 2/1/3,这样就能减少一次寄存器的存取次数。

int x = 1;// 步骤 1:加载 x 变量的内存地址到寄存器中,加载 1 到寄存器中,CPU 通过 mov 指令把 1 写入到寄存器指定的内存中
boolean flag = true; // 步骤 2 加载 flag 变量的内存地址到寄存器中,加载 true 到寄存器中,CPU 通过 mov 指令把 1 写入到寄存器指定的内存中
int y = x + 1;// 步骤 3 重新加载 a 变量的内存地址到寄存器中,加载 1 到寄存器中,CPU 通过 mov 指令把 1 写入到寄存器指定的内存中

在 JVM 中,重排序是十分重要的一环,特别是在并发编程中。可 JVM 要是能对它们进行任意排序的话,也可能会给并发编程带来一系列的问题,其中就包括了一致性的问题。

3、Happens-before 规则

为了解决这个问题,Java 提出了 Happens-before 规则来规范线程的执行顺序:

  • 程序次序规则:在单线程中,代码的执行是有序的,虽然可能会存在运行指令的重排序,但最终执行的结果和顺序执行的结果是一致的;
  • 锁定规则:一个锁处于被一个线程锁定占用状态,那么只有当这个线程释放锁之后,其它线程才能再次获取锁操作;
  • volatile 变量规则:如果一个线程正在写 volatile 变量,其它线程读取该变量会发生在写入之后;
  • 线程启动规则:Thread 对象的 start() 方法先行发生于此线程的其它每一个动作;
  • 线程终结规则:线程中的所有操作都先行发生于对此线程的终止检测;
  • 对象终结规则:一个对象的初始化完成先行发生于它的 finalize() 方法的开始;
  • 传递性:如果操作 A happens-before 操作 B,操作 B happens-before 操作 C,那么操作 A happens-before 操作 C;
  • 线程中断规则:对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生。

结合这些规则,我们可以将一致性分为以下几个级别:

严格一致性(强一致性):所有的读写操作都按照全局时钟下的顺序执行,且任何时刻线程读取到的缓存数据都是一样的,Hashtable 就是严格一致性;

img

顺序一致性:多个线程的整体执行可能是无序的,但对于单个线程而言执行是有序的,要保证任何一次读都能读到最近一次写入的数据,volatile 可以阻止指令重排序,所以修饰的变量的程序属于顺序一致性;

img

弱一致性:不能保证任何一次读都能读到最近一次写入的数据,但能保证最终可以读到写入的数据,单个写锁 + 无锁读,就是弱一致性的一种实现。

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

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

相关文章

【Docker实操】创建一个Nginx服务

一、获取nginx官方镜像 docker pull nginx //拉取nginx官方镜像 docker image nginx //查看镜像二、创建项目目录 项目目录:/root/www2/nginx //如果当前目录在root mkdir www2 mkdir www2/nginx cd www2/nginx //进入项目目录三、创建源码文件 //当前目录&…

jsp高校教师调课管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 高校教师调课管理系统是一套完善的java web信息管理系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发,数据库为Mysq…

react native 环境准备

一、必备安装 1、安装node 注意 Node 的版本应大于等于 16,安装完 Node 后建议设置 npm 镜像(淘宝源)以加速后面的过程(或使用科学上网工具)。 node下载地址:Download | Node.js设置淘宝源 npm config s…

GEE:均值滤波

作者:CSDN @ _养乐多_ 本文将介绍在 Google Earth Engine(GEE)平台上,进行均值滤波操作的代码框架、核心函数和多种卷积核。 并分别以林地区域和农田区域为试验区,以NDVI图像为例。结果如下图所示, 文章目录 一、均值滤波二、完整代码三、代码链接一、均值滤波 均值滤…

大数据实战项目_电商推荐系统

一、 项目介绍 HadoopSpark (Python)Scala SparkSQLSparkStreaming MongoDB Redis Kafka Flume ( SpringMVC vue) 1 项目介绍 1.1 项目系统架构 项目以推荐系统建设领域知名的经过修改过的中文亚马逊电商数据集作为依托,以某电商网站真实业务数据架构为基…

代码随想录刷题题Day3

刷题的第三天,希望自己能够不断坚持下去,迎来蜕变。😀😀😀 刷题语言:C / Python Day3 任务 ● 链表理论基础 ● 203.移除链表元素 ● 707.设计链表 ● 206.反转链表 1 链表理论基础 链表:通过…

【ArcGIS Pro微课1000例】0045:深度学习--车牌模糊

借助ArcGIS Pro提供的车牌模糊训练模型,可以很方便实现车牌模糊。 文章目录 一、车牌模糊对比二、工具介绍三、案例实现一、车牌模糊对比 车牌模糊前: 车牌模糊后: 二、工具介绍 本功能使用的依然是ArcGIS Pro提供的深度学习工具中的使用深度学习分类像素(Classify Pixel…

【PUSDN】java中easyexcel导入导出带有图片的Excel(main方法方式)

简述 java中easyexcel导入导出带有图片的Excel(main方法方式),web方式详见另一篇 由于电脑音频问题,视频暂时没有解说声音, 回头重新补上 前情提示 如果有任何疑问、需求、技术支持,欢迎点赞&#xff0…

智能优化算法应用:基于松鼠算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于松鼠算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于松鼠算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.松鼠算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

精准长尾关键词批量挖掘工具,长尾关键词挖掘正确使用方法

互联网时代,SEO已然成为网站推广的关键一环。而在SEO的世界里,长尾关键词无疑是一块被广泛忽视却蕴含着巨大潜力的宝地。 什么是长尾关键词 长尾关键词,指的是那些相对不那么热门、搜索量较低但更为具体、更贴近用户真实需求的关键词。与短…

Knowledge Review(CVPR 2021)论文解析

paper:Distilling Knowledge via Knowledge Review official implementation:https://github.com/dvlab-research/ReviewKD 前言 识蒸馏将知识从教师网络转移到学生网络,可以提高学生网络的性能,作为一种“模型压缩”的方法被…

vscode调试mit6s081Lab

环境 mit6.s081的实验环境gdb-multiarch(用于gdb调试,vscode调试实质上就是提供个图形化页面,底层还是这个) // 安装 gdb-multiarch sudo apt-get install gdb-multiarch大家好,我叫徐锦桐,个人博客地址为…

css如何设置文本添加下划线

css文本添加下划线 text-decoration: underline;text-decoration相关属性参数 参数描述none默认。定义标准的文本。underline定义文本下的一条线。overline定义文本上的一条线。line-through定义穿过文本下的一条线。blink定义闪烁的文本。inherit规定应该从父元素继承 text-…

组件的props属性

目录 1:使用props的作用: 2:props自定义属性的用法: 3:集合v-bind使用自定义属性: 4:props自定义属性是只读的: 5:default默认值: 6:type值类…

文件操作--IO

目录 ♫什么是文件 ♫文件路径 ♫文件类型 ♫文件的管理 ♪File的构造方法 ♪File的常用方法 ♫文件的内容操作 ♪InputStream ♪OutputStream ♪字符流读写文件 ♫Scanner与流对象 ♫什么是文件 文件在计算机里可以指“狭义”的文件(指硬盘上的文件和目录&…

深层神经网络(第四周)

这里省略了深层神经网络的前向传播和反向传播,内容和之前相似,不做过多描述。若今后需要,可以再补习。 一、为什么使用深层表示 解决问题时其实并不需要很大的神经网络,但是得有深度,得有比较多的隐藏层。这是为什么…

LeetCode(49)用最少数量的箭引爆气球【区间】【中等】

目录 1.题目2.答案3.提交结果截图 链接: 用最少数量的箭引爆气球 1.题目 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] [x_start, x_end] 表示水平直径在 x_start 和 x_end之间的气球。你不知道气…

基于OpenAPI工具包以及LSTM的CDN网络流量预测

基于LSTM的CDN网络流量预测 本案例是基于英特尔CDN以及英特尔 OpenAPI Intel Extension for TensorFlow* Intel oneAPIDPC Library 的网络流量预测,CDN是构建在现有网络基础之上的智能虚拟网络,目的是将源站内容分发至最接近用户的节点,使用…

【JVM】一篇通关JVM类加载与字节码技术

目录 1. 类文件结构1-1. 魔数 版本 常量池 2. 字节码指令3. 编译期处理4. 类加载阶段5. 类加载器6. 运行期优化 类加载与字节码技术 1. 类文件结构 案例 // HelloWorld 示例 public class HelloWorld {public static void main(String[] args) {System.out.println("h…

淘宝商家店铺注册流程

如果本身已经有淘宝账号了,直接从第三步骤:创建店铺开始就可以了。一、注册淘宝账号、支付宝账号 首先,如果你有买家账号,是可以直接拿来开店的,不用单独在注册淘宝账号开店。 注意事项: 1:淘宝账号注册后,不能修改,如果会员名有写错情况&a…