JUC并发编程与源码分析笔记06-Java内存模型之JMM

news2024/12/25 15:13:28

计算机硬件存储体系

CPU的运行并不是直接操作内存而是先把内存里边的数据读到缓存,而内存的读和写操作的时候就会造成不一致的问题。
JVM规范中试图定义一种Java内存模型(Java Memory Model,简称JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。

Java内存模型Java Memory Model

JMM(Java内存模型Java Memory Model,简称JMM)本身是一种抽象的概念并不真实存在它仅仅描述的是一组约定或规范,通过这组规范定义了程序中(尤其是多线程)各个变量的读写访问方式并决定一个线程对共享变量的写入何时以及如何变成对另一个线程可见,关键技术点都是围绕多线程的原子性、可见性和有序性展开的。
通过JMM来实现线程和主内存之间的抽象关系。
屏蔽各个硬件平台和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到一致的内存访问效果。

JMM规范下,三大特性

可见性

当一个线程修改了某一共享变量的值,其他线程能否立刻知道该变更,JMM规定了所有变量都存储在主内存中。
系统主内存共享变量数据修改被写入的时机是不确定的,多线程并发下很可能出现“脏读”,所以每个线程都有自己的工作内存、线程自己的工作内存中保存了该线程使用到的变最的主内存副本拷贝。线程对变量的所有操作(读取,赋值等)都必需在线程自己的工作内存中进行,而不能够直接读写主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

原子性

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

有序性

对于一个线程的执行代码而言,我们总是习惯性认为代码的执行总是从上到下,有序执行。但为了提升性能,编译器和处理器通常会对指令序列进行重新排序。Java规范规定JVM线程内部维持顺序化语义,即只要程序的最终结果与它顺序化执行的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序。
JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。但是,指令重排可以保证串行语义一致,但没有义务保证多线程间的语义也一致(即可能产生“脏读”),简单说,两行以上不相干的代码在执行的时候有可能先执行的不是第一条,不见得是从上到下顺序执行,执行顺序会被优化。
从源码到最终执行顺序示意图:源代码→编译器优化的重排→指令并行的重排→内存系统的重排→最终执行的指令。
单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。处理器在进行重排序时必须考虑指令之间的数据依赖性。

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

读取过程

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

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

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

总结

我们定义的所有共享变量都储存在物理主内存中。
每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
线程对共享变量所有的操作都必须先在线程自己的工作内存中进行后写回主内存,不能直接从主内存中读写(不能越级)
不同线程之间也无法直接访问其他线程的工作内存中的变量,线程间变量值的传递需要通过主内存来进行(同级不能相互访问)

JMM规范下,多线程先行发生原则之happens-before

在JMM里,如果一个操作执行的结果需要对另一个操作可见,或者代码重排序,那么这两个操作之间必须存在happends-before(先行发生)原则。
如果Java内存模型中所有的有序性都仅靠volatile和synchronized来完成,那么有很多操作都将会变得非常啰嗦,但是我们在编写Java并发代码的时候并没有察觉到这一点。
我们没有时时、处处、次次,添加volatile和synchronized来完成程序,这是因为Java语言中JMM原则下,有一个“先行发生”(Happens-Before)的原则限制和规矩,给你立好了规矩!
这个原则非常重要:
它是判断数据是否存在竞争,线程是否安全的非常有用的手段。依赖这个原则,我们可以通过几条简单规则一揽子解决并发环境下两个操作之间是否可能存在冲突的所有问题,而不需要陷入Java内存模型苦涩难懂的底层编译原理之中。

happens-before总原则

如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个之前。
两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则来执行,如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。

happens-before之8条

次序规则

一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作。前一个操作的结果,可以被后一个操作获取。

锁定规则

一个unlock操作先行发生于后面(时间上的先后)对同一个锁的lock操作。也就是先有对对象的lock操作,再有对对象的unlock()操作,反之不行。

volatile变量规则

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

传递规则

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

线程启动规则(Thread Start Rule)

Thread对象的start()方法先行发生于此线程的每一个动作,也就是先调用start()方法,线程里的动作才会执行。

线程中断规则(Thread Interruption Rule)

对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生,也就是先调用interrupt()方法设置过中断标志位,才能通过Thread.interrupted()检测到中断发生。

线程终止规则(Thread Termination Rule)

线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过isAlive()等手段检测线程是否已经终止。

对象终结规则(Finalizer Rule)

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

happens-before小总结

在Java语言里面,Happens-Before的语义本质上是一种可见性。
A Happens-Before B意味着A发生过的事情对B来说是可见的,无论A事件和B事件是否发生在同一个线程里。
JMM的设计分为两部分:
一部分是面向我们程序员提供的,也就是happens-before规则,它通俗易懂的向我们程序员阐述了一个强内存模型,我们只要理解happens-before规则,就可以编写并发安全的程序了。
另一部分是针对JVM实现的,为了尽可能少的对编译器和处理器做约束从而提高性能,JMM在不影响程序执行结果的前提下对其不做要求,即允许优化重排序。我们只需要关注前者就好了,也就是理解happens-before规则即可,其它繁杂的内容有JMM规范结合操作系统给我们搞定,我们只写好代码即可。

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

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

相关文章

电平触发的触发器

普通的SR锁存器没有任何抗干扰能力 我们要加控制信号,来抵抗干扰 比如说我们不把信号直接加在门上,我们可以再加一级门电路,让这个输出和输入不在同一个门上,我们希望加入一个控制信号,来控制电路工作的时刻 对电路结…

神经网络——反向传播算法

一、多元分类 之前讨论的神经网络都是以二元分类为目的进行介绍的。 当我们有不止两种分类时(也就是y1,2,3….y1,2,3….y1,2,3….),比如以下这种情况,该怎么办?如果我们要训练一个神经网络算法来识别路人、汽车、摩托…

mysql的主从复制与读写分离

目录 一. MySQL 主从复制原理 1.1 MySQL 支持的复制类型 1.2 MySQL主从复制的工作过程 二、主从复制实验部署 2.1、实验环境 2.2、mysql主从服务器时间同步 主服务器(192.168.80.11) 从服务器(192.168.80.12/13) 2.3、主…

[附源码]计算机毕业设计网上鲜花购物系统Springboot程序

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

【机器学习】支持向量回归

有任何的书写错误、排版错误、概念错误等,希望大家包含指正。 在阅读本篇之前建议先学习: 【机器学习】支持向量机【上】硬间隔 【机器学习】支持向量机【下】软间隔与核函数 支持向量回归 支持向量回归(support vector regression&#xf…

[附源码]计算机毕业设计基于SpringBoot的党务管理系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

《人类简史》笔记三—— 历史从无正义

目录 一、尽管把人人生而平等喊得震天响,其实还是把人分成了上下等级 二、恶性循环 三、当男人究竟有什么好的? 一、尽管把人人生而平等喊得震天响,其实还是把人分成了上下等级 古时候: 上等人 平民和奴隶 现在:…

网络结构模式,协议,端口,网络模型,arp

网络结构模式(软件结构) C/S结构 服务器 - 客户机,即 Client - Server(C/S)结构 C/S 结构通常采取两层结构: 服务器负责数据的管理客户机负责完成与用户的交互任务 在C/S结构中,应用程序分为两部分: 服务…

koa 和 express 的对比

前言 天冷了,唯有学习来温暖自己。 最近利用业余的时间,跟着 coderwhy 老师学习 node.js,了解以及掌握一些服务端的常见知识: fileSystem:文件读取模块。events:事件流Buffer:node 中处理二进…

高仿英雄联盟游戏网页制作作业 英雄联盟LOL游戏HTML网页设计模板 简单学生网页设计 静态HTML CSS网站制作成品

🎉精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

Java—代理

文章目录先举一个例子:开闭原则什么是代理两种调用的方式:代理定义:代理的实现方式静态代理代理的目的:如何让程序知道要增强的功能是谁?静态代理缺点:动态代理什么是动态代理动态代理的实现静态代理和动态…

26. SAP ABAP OData Gateway 框架里 /IWFND, /IWBEP 这些缩写代表了什么含义?

文章目录 GWFNDBEP本文参考链接本教程前一篇文章 - 25. 答疑 - SAP OData 框架处理 Metadata 元数据请求的实现细节,前后端组件部署在同一台物理服务器,我们深入了 SAP ABAP OData 框架来学习其元数据请求的执行明细。 我们在文章里看到了不少以 /IWFND/, /IWBEP 这些命名空…

MAUI Blazor (Windows) App 动态设置窗口标题

原文链接 https://www.cnblogs.com/densen2014/p/16950996.html 接着上一篇"如何为面向 Windows 的 MAUI Blazor 应用程序设置窗口标题?" Tips: 总所周知,MAUI 除了 Windows App 其他平台窗口是没有 Title 这回事的. 在 Blazor 里面可以直接给页面打上…

时间轴-新年倒计时(实操java)

文章目录一、前言二、前端代码实现1、效果图年月日倒计时秒杀1天倒计时秒杀60秒倒计时2、代码实操(微信小程序前端)①在utils文件夹下新建js文件:getperiod.js工具类②引入js,在页面index.js开头引入③完整代码3、倒计时实现①1天…

学习python第一天(数据类型)

关于Python的数据类型 Python数据类型包括: 数字类型,字符类型,布尔类型,空类型,列表类型,元组类型,字典类型 1、数字类型 包括:整型int 浮点型float(有小数位的都是是浮点型) 注…

自己写Spring组件-01手写Bean注册器

简介 在进行web开发的时候,我们经常使用Component、Services、Controller等注解去声明Bean,然后Spring就会帮我们注册。 如果我们想要自己写一个组件(方便后期使用),进行无感引入的时候,就需要自己去注册B…

【计算机网络】计算机网络复习总结 ----- 链路层

计算机网络 内容管理数据链路层 Data Link Layer相关概念术语link 链路data link 数据链路帧 frameMTU maximum transfer Unit 最大传送单元PPP point to point Protcol 点对点协议MAC 地址 media access control网桥 bridge交换机 switch成帧 规则成帧定界字符计数法字符填充分…

QT:文件介绍

UI:QT程序中使用的ui界面就保存在该文件中 组件 设置当前组件的属性 点谁显示谁的属性 调整最终页面的布局,使组件控件在水平或对齐 调整因页面放大缩小时,控件与控件之间的水平和垂直间距 各种各样的按钮 图像显示有关的视图 列表框 各…

Gradle学习笔记之Groovy简单使用

简介 groovy可以当成java的脚本化改良版,同样运行于JVM之上,可以很好地和java代码及相关库进行交互,既可以面向对象编程,也可以用作纯粹的脚本语言。Groovy支持动态类型转换、闭包、元编程、函数式编程、默认作用域为public&…

ArcGIS基础:使用线段分割面数据操作

本操作主要是实现线要素对面要素的分割。 先看一下原始数据,如下所示,2个面,1条线,面被线贯穿过去。 首先进行【面转线】操作,以提取面要素的边界线,如下所示 提取结果如下所示: 然后进行【…