【JUC】Java内存模型之JMM

news2025/1/18 9:01:49

【JUC】Java内存模型之JMM

文章目录

  • 【JUC】Java内存模型之JMM
    • 1. 概念
    • 2. JMM三大特性
      • 2.1 可见性
      • 2.2 原子性
      • 2.3 有序性
    • 3. 多线程对变量的读写过程
    • 4. 先行发生原则——happens-before
      • 4.1 happens-before八条规则
        • 4.1.1 次序规则
        • 4.1.2 锁定规则
        • 4.1.3 volatile变量规则
        • 4.1.4 传递规则
        • 4.1.5 线程启动规则
        • 4.1.6 线程中断规则
        • 4.1.7 线程终止规则
        • 4.1.8 对象终结规则

1. 概念

JMM:Java内存模型 Java Memory Model ,简称JMM。本身是一种抽象的概念,并不真实存在,它仅仅描述的是一组约定或规范,通过这组规范定义了程序中(尤其是多线程)各个变量的读写访问方式并决定一个线程对共享变量的写入何时以及如何变成对另一个线程可见,关键技术点都是围绕多线程的原子性、可见性和有序性展开的。

作用

  1. 通过JMM来实现线程和主内存之间的抽象关系
  2. 屏蔽各个硬件平台和操作系统的内存访问差异以实现让Java程序在各个平台下都能达到一致的内存访问效果。

2. JMM三大特性

  1. 可见性
  2. 原子性
  3. 有序性

2.1 可见性

可见性:是指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道该变更 ,JMM规定了所有的变量都存储在主内存中。

image-20230413231820592

Java中普通的共享变量不保证可见性,因为数据修改被写入内存的时机是不确定的,多线程并发下很可能出现"脏读",所以每个线程都有自己的工作内存,线程自己的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取,赋值等 )都必需在线程自己的工作内存中进行,而不能够直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

image-20230413232014966

如果没有可见性保证的话会出现线程脏读:

  1. 主内存中有变量x,初始值为0
  2. 线程A想要将x加1,先将x=0拷贝到自己的私有内存中,然后更新x的值
  3. 线程A将更新后的x值回刷到主内存的时间是不固定的
  4. 刚好在线程A没有回刷x到主内存时,线程B同样从主内存中读取x,此时为0,和线程A一样的操作,最后期盼的x=2就会变成x=1

2.2 原子性

原子性:指一个操作是不可中断的,即多线程环境下,操作不能被其他线程干扰。

线程修改共享变量的时候要保证原子性。


2.3 有序性

对于一个线程的执行代码而言,我们总是习惯性认为代码的执行总是从上到下,有序执行。但是为了提高性能,编译器和处理器通常会对执行序列进行重排序。指令重排可以保证串行语义一致,但没有义务保证多线程间的语义也一致,即可能产生 脏读 ,简单说,两行以上不相干的代码在执行的时候有可能先执行的不是第一条,不见得是从上到下顺序执行,执行顺序会被优化。

image-20230413233253333

单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。

处理器在进行重排序时必须要考虑指令间的数据依赖性

多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。


3. 多线程对变量的读写过程

JMM规范下,多线程对变量的读写过程:

由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到的线程自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图:

image-20230413230537513

JMM定义了线程和主内存之间的抽象关系

  1. 线程之间的共享变量存储在主内存中(从硬件角度来说就是内存条)
  2. 每个线程都有一个私有的本地工作内存,本地工作内存中存储了该线程用来读/写共享变量的副本(从硬件角度来说就是CPU的缓存,比如寄存器、L1、L2、L3缓存等)

4. 先行发生原则——happens-before

概述:如果一个操作A happens-before 另一个操作B,那么操作A对共享变量的修改将对操作B可见,并且在程序执行中,操作A将在操作B之前被执行。这个规则可以避免因为线程执行顺序不确定而导致的数据竞争和内存一致性问题。

注:两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happends-before关系来执行的结果一致,那么这种重排序并不非法。


4.1 happens-before八条规则

4.1.1 次序规则

一个线程内,按照代码顺序,写在前面的操作先行发生于后面的操作。

简单来说就是前一个操作的结果可以被后续的操作获取,前面一个操作把变量x的值赋为1,那么后面一个操作肯定能知道x已经变成了1。


4.1.2 锁定规则

一个unLock操作先行发生于后面(这里的“后面”指的是时间上的先后)对同一个锁的lock操作。


4.1.3 volatile变量规则

对一个volatile变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的,这里的“后面”同样是指时间上的先后。


4.1.4 传递规则

如果操作A先行发生与操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C。


4.1.5 线程启动规则

Thread对象的start()方法先行发生于此线程的每一个动作。


4.1.6 线程中断规则

对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。

也就是说要先调用interrupt()方法设置中断标志位,才能检测到中断发生。


4.1.7 线程终止规则

线程中所有操作都先行发生于对此线程的终止检测,我们可以通过Thread::join()方法判断是否结束,Thread::isAlive()的返回值等手段检测线程是否已经终止执行。


4.1.8 对象终结规则

一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始,简单来说就是对象没有完成初始化之前,是不能调用finalized()方法的。


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

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

相关文章

【Unity入门】13.脚本外置参数

【Unity入门】脚本外置参数 大家好,我是Lampard~~ 欢迎来到Unity入门系列博客,所学知识来自B站阿发老师~感谢 (一)外置脚本参数 (1)外置自转脚本的速度参数 我们在RotateLogic的时候,为了实现自…

ubuntu虚拟机下搭建zookeeper集群,安装jdk压缩包,搭建Hadoop集群与spark集群的搭建【下篇】

系列文章目录 Hadoop与主机连接以及20版本的Hadoop配置网络的问题_hadoop连不上网 Hadoop升级update命令被锁定的解决方法_hadoop重新初始化被锁住怎么办虚拟机vmware下安装Ubuntu16.04修改屏幕尺寸与更新源,以及对应的安装vim和vim常见的操作命令 文章目录 前言…

ELK部署-实现Nginx日志收集

一、部署ES 1、创建网络下载镜像 docker network create elastic docker pull elasticsearch:7.17.62、目录准备 mkdir /opt/ELK/elastic/{data,config} -p chmod 777 /opt/ELK/elastic/datacat >> /opt/ELK/elastic/config/elasticsearch.yml <<EOF cluster.na…

DFS与BFS寻找图中的所有路径(C++)

文章目录图的存储理论知识数组模拟链表数组模拟邻接表DFS 寻找所有路径代码输入数据对应图输出BFS 寻找所有路径代码输入数据对应图输出备注写在后面图的存储 理论知识 图的存储主要有 2 种方式 邻接表邻接矩阵 邻接矩阵不适合存储稀疏图&#xff0c;本文使用邻接表来存储图 …

运用Navicat 实现 DML(对表的数据进行增删改)

如何使用Navicat呢&#xff1f; 当Navicat配置好后&#xff0c;链接上数据库后。 点击查询后tables中的任意一个新建查询&#xff0c;这时就会跳出一个查询编辑器。 我在初始sql是就创建了stu表。这里就不创建了。 先选择需要的表&#xff0c; select * from 表名; 添加&…

【JAVA】经典面试题:HashMap,Hashtable和ConcurrentHashMap三者之间的区别!!!

本篇的内容是围绕哈希表来展开的&#xff0c;主要是通对HashMap&#xff0c;Hashtable&#xff0c;ConcurrentHashMap三者的特点去了解这它们之间的区别以及运用场景 目录 1. HashMap 2. Hashtable 锁太粗问题&#xff1a; 3. 扩容机制问题 3. ConcurrentHashMap Concurr…

N5183B信号发生器

N5183B N5183B,是德keysight N5183B 主要特性与技术指标信号特征9 kHz &#xff5e; 3 或 6 GHz在 3 GHz 时提供 24 dBm 功率&#xff0c;带有电子衰减器1 GHz 和 20 kHz 偏置时&#xff0c;相位噪声为 -146 dBc≤-73 dBc ACP W-CDMA 64 DPCH 和 <0.4% EVM 160 MHz 802.11…

万字长文解读Stable Diffusion的核心插件—ControlNet

目录 一、介绍 二、使用方法 三、ControlNet结构 1.整体结构 2.ControlLDM 3.Timestep Embedding 4.HintBlock 5.ResBlock 6.SpatialTransformer 7.SD Encoder Block 8.SD Decoder Block 9.ControlNet Encoder Block 10.Stable Diffusion 四、训练 1.准备数据集…

stable-diffusion-webui浅叙

GitHub - AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI 使用Git下载&#xff1a; git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git 运行 webui-user.bat : git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.g…

【NestJs】使用MySQL创建多个实体

如果小伙伴还不会使用nestjs连接数据库的话 可以看我的上一篇文章 NestJs使用连接mysql企业级开发规范 关系 关系是指两个或多个表之间的联系。关系基于每个表中的常规字段&#xff0c;通常包含主键和外键。关系有三种&#xff1a; 名称说明一对一主表中的每一行在外部表中有…

从零到一发布 NPM 包

如果你负责前端的基础能力建设&#xff0c;发布各种功能/插件包犹如家常便饭&#xff0c;所以熟悉对 npm 包的发布与管理是非常有必要的&#xff0c;故此有了本篇总结文章。本篇文章一方面总结&#xff0c;一方面向社区贡献开箱即用的 npm 开发、编译、发布、调试模板&#xff…

【展会邀请】百华与您相约第104届中国劳动保护用品交易会!

重磅消息&#xff01;一场行业极具规模的劳保展 第104届中国劳动保护用品交易会 暨2023中国国际职业安全及健康产业博览会 将于2023.4.13-15在上海新国际博览中心E1-E7馆隆重举办&#xff01; 山东百华鞋业有限公司受邀参展&#xff0c;正在火热筹备中。 百华展位号 2023…

算法:将一个数组旋转k步

题目 输入一个数组如 [1,2,3,4,5,6,7]&#xff0c;输出旋转 k 步后的数组。 旋转 1 步&#xff1a;就是把尾部的 7 放在数组头部前面&#xff0c;也就是 [7,1,2,3,4,5,6]旋转 2 步&#xff1a;就是把尾部的 6 放在数组头部前面&#xff0c;也就是 [6,7,1,2,3,4,5]… 思路 思…

PasteSpider的下载和安装

你是否在纠结于k8s的庞大和复杂&#xff0c;是否在被混论的发布流程搞得焦头烂额。PasteSpider适合你&#xff01;足够小的内存资源消耗(300MB甚至更低&#xff01;)&#xff0c;不需要专业的运维知识&#xff0c;图文操作&#xff0c;支持一键发布&#xff0c;支持自动路由配置…

泛型基本说明

使用传统方法的问题分析 不能对加入到集合ArrayList中的数据类型进行约束&#xff08;不安全&#xff09;遍历的时候&#xff0c;需要进行类型转换&#xff0c;如果集合中的数据量较大&#xff0c;对效率有影响。泛型的好处 编译时&#xff0c;检查添加元素的类型&#xff0c;提…

springbean 的 setter/构造注入

文章目录前言一、另外两种注入的怎么用&#xff1f;二、使用setter和构造注入的步骤1. 搞一个配置类,用户获取spring容器中的bean2. 由于有静态方法,所以直接调用三、使用final 的构造注入方式(推荐)总结前言 我们知道,一般java中的依赖注入有三种: 1 属性注入 2 settter注入 …

Golang每日一练(leetDay0039) 二叉树专题(8)

目录 115. 不同的子序列 Distinct Subsequences &#x1f31f;&#x1f31f;&#x1f31f; 116. 填充每个节点的下一个右侧节点指针 Populating-next-right-pointers-in-each-node &#x1f31f;&#x1f31f; 117. 填充每个节点的下一个右侧节点指针 II Populating-next-ri…

模拟信号放大转换器 非隔离 线性对应输入输出 大功率负载

概述&#xff1a; 导轨安装DIN11 NIPO 系列模拟信号放大器是一种将输入信号放大、转换成按比例输出的直流信号放大器。产品广泛应用在电力、远程监控、仪器仪表、医疗设备、工业自控等需要直流信号测控的行业。此系列产品内部采用稳压电路&#xff0c;通过等比例控制线性放大输…

Threshold ECDSA——web3.0开发中的门限签名

多重签名 1.联名账户&#xff0c;任何一个密钥都能打开账户。 2.储蓄账户&#xff0c;需要所有密钥才能打开账户。 3.级联账户&#xff0c;可以使用部分密钥做部分功能&#xff0c;需要所有密钥才能执行全部功能。 4.在加密货币中&#xff0c;多重签名通过创建一个多重签名…

超详细从入门到精通,pytest自动化测试框架实战-fixture多样玩法(九)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 在编写测试用例&…