深入理解JMM以及并发三大特性(1)

文章目录

    • 1. 并发与并行
    • 2. JMM
    • 3. 并发三大特性
    • 4.总结

1. 并发与并行

并行:指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是宏观来看,二者都是一起执行的。
并发:指在同一时刻只能有一个指令执行,但多个进程指令被快速的轮换执行,使得宏观上具有多个进程同时执行的效果,但在围观上并不是同时执行,只是把时间分为若干段,使多个进程快速交替执行。

目标都是最大化CPU的使用率

2. JMM

JMM也就是java内存模型,它和JVM的内存模型不是一个概念,JMM是与线程间通信有关的,并且它是一块共享内存模型。Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM),用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,JMM规范了Java虚拟机与计算机内存是如何协同工作的:规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。JMM 描述的是一种抽象的概念,一组规则,通过这组规则控制程序中各个变量在共享数据区域和私 有数据区域的访问方式,JMM是围绕原子性、有序性、可见性展开的。
在这里插入图片描述
一个线程是如何修改共享变量的?首先线程需要从主存中拷贝一个副本到自己的工作内存的栈帧中,然后进行修改,修改后再推送到主内存。(实际上是本地内存和主内存的交互)

3. 并发三大特性

并发编程一些问题的源头都来自于并发编程的三大特性,即:可见性、原子性和有序性。

  • 可见性

当一个线程修改了共享变量值,其他线程能够看到修改的值。Java内存模型时通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方法来实现可见性。

如何保证可见性

  1. 通过volatile关键字保证可见性
  2. 通过内存屏障保证可见性
  3. 通过sychronized保证可见性
  4. 通过Lock保证可见性
  5. 通过final关键字保证可见性

如下面代码:

public class VisibilityTest {private boolean flag = true;private int count = 0;public void refresh() {flag = false;System.out.println(Thread.currentThread().getName() + "修改flag:"+flag);}public void load() {System.out.println(Thread.currentThread().getName() + "开始执行.....");while (flag) {//TODO  业务逻辑count++;}System.out.println(Thread.currentThread().getName() + "跳出循环: count=" + count);}public static void main(String[] args) throws InterruptedException {VisibilityTest test = new VisibilityTest();// 线程threadA模拟数据加载场景Thread threadA = new Thread(() -> test.load(), "threadA");threadA.start();// 让threadA执行一会儿Thread.sleep(1000);// 线程threadB通过flag控制threadA的执行时间Thread threadB = new Thread(() -> test.refresh(), "threadB");threadB.start();}public static void shortWait(long interval) {long start = System.nanoTime();long end;do {end = System.nanoTime();} while (start + interval >= end);

线程A调用load方法不断修改count的值,线程B修改flag来终止线程A的循环,但由于JMM的关系,线程B对flag的修改线程A看不到(线程A会一直使用其副本,不会读主存的新值),就一直会死循环,这就是可见性问题导致的。
在这里插入图片描述

volatile关键字

private volatile boolean flag = true;

volatile底层是由c++实现的。如下面Hotspot源码,它首先判断flag是不是valatile关键字修饰的,如果是就调用后面的storeload()方法(这个本质上就是内存屏障,这是JVM层面的内存屏障)。
在这里插入图片描述
进入storeload方法,由于java的跨平台特性,jvm底层对不同的系统架构提供了不同的storeload的实现,这里我们看x86架构。storeload方法会调用fence方法,然后fence方法执行了一句lock; addl $0,0(%%rsp),这是一条汇编指令,这条指令称为lock前缀指令。下面介绍一下这条指令的作用:

  1. 确保后续指令执行的原子性。在Pentium及之前的处理器中,带有lock前缀的指令在执行期间会锁住总线,使得其它处理器暂时无法通过总线访问内存,很显然,这个开销很大。在新的处理器中,Intel使用缓存锁定来保证指令执行的原子性,缓存锁定将大大降低 lock前缀指令的执行开销。
  2. LOCK前缀指令具有类似于内存屏障的功能,禁止该指令与前面和后面的读写指令重排序。
  3. LOCK前缀指令会等待它之前所有的指令完成、并且所有缓冲的写操作写回内存(也就是将storebuffer中的内容写入内存)之后才开始执行,并且根据缓存一致性协议,刷新 storebuffer的操作会导致其他cache中的副本失效(缓冲失效)。

这就是Volatile实现可见性和有序性的原理。
在这里插入图片描述
上面代码介绍了字节解释器执行volatile关键字的过程,字节解释器用C++实现了JVM指令,其优点是实现相对简单且容易理解,缺点是执行慢。由于字节码解释器执行速度很慢,所以jvm基本不采用上述方式来执行代码,而是使用模版解释器(可以理解为c++的JIT技术),模版解释器对每个指令都写了一段对应的汇编代码,启动时将每个指令与对应的汇编代码入口绑定,可以说效率达到了极致,底层还是加了lock; addl $0,0(%%rsp)汇编指令,我们从汇编指令可以看出来。
在这里插入图片描述

内存屏障

public void load() {while (flag) {//TODO  业务逻辑count++;}//这里就设置了内存屏障UnsafeFactory.getUnsafe().storeFence();     }

UnsafeFactory.getUnsafe().storeFence();底层和volatile一样,都是调用了storeload和fence方法,即加入了lock前缀指令。
利用本地变量自动淘汰机制

public void load() {while (flag) {//TODO  业务逻辑count++;shortWait(1000000);}}

调用shortWait后,线程A会陷入等待,然后过一段时间后,本地副本的flag会被淘汰(需要达到一定的时间,时间过短是不会被淘汰的),所以while(flag)再次取flag的值会从主存中取,所以会线程A会被结束,这也提供了一种解决可见性的思路。

利用Thread.yield()方法

Thread.yield() 是一个静态方法,它的目的是让当前线程让出 CPU 资源,使得其他具有相同优先级的线程有机会运行。调用 Thread.yield() 将当前线程从运行状态转为就绪状态,然后重新竞争 CPU 资源。在 Java 中,Thread.yield() 的底层实现可以因平台而异,因为它依赖于底层操作系统的线程调度机制。一般来说,Thread.yield() 可能会调用底层操作系统提供的让出 CPU 资源的原语。在许多系统中,这可能是通过设置线程的状态为就绪状态并将其放置在就绪队列中来实现的。然后,操作系统的调度器可以选择从就绪队列中选择另一个线程来运行。

Thread.yield()会让线程A让出当前的CPU资源(时间片),这个过程会发生线程状态转换(线程上下文切换),线程进行上下文切换的时候会保存当前线程的线程,如果线程A又竞争到CPU时间片,它就需要需要还原自己的线程,这个时候线程A就需要重写加载自己的上下文,所以可以从主存加载到修改后的flag值,这就解决了可见性问题。上下文切换的图解为:

public void load() {while (flag) {//TODO  业务逻辑count++;Thread.yield();}}

加入标准输出流

public void load() {while (flag) {//TODO  业务逻辑count++;System.out.println();}}

System.out.println();为什么可以实现flag的可见性,这就和System.out.println();底层有关系了:

public void println(char x[]) {synchronized (this) {print(x);newLine();}}

可以看见println方法底层加入了重量级锁synchronized,而它本身就是可以保证原子性、可见性和有序性的。其实synchronized底层也是靠内存屏障来实现的,同样会调用storeload和fence来加入内存屏障来实现可见性的。

使用unpark

public void load() {while (flag) {//TODO  业务逻辑count++;LockSupport.unpark(Thread.currentThread);}}

其实它本质上也是内存屏障

使用sleep()方法

public void load() {while (flag) {//TODO  业务逻辑count++;Thread.sleep(1000);}}

我们看sleep底层源码

在这里插入图片描述
其实它底层也是调用了fence方法,加入内存屏障,所以同样它也可以实现可见性。

使用Integer关键字

private Integer count = 0;

我没看底层的Integer源码,我们知道Integer会定义一个value值来保存需要保证的int值,而这个值本身是一个final常量,而jvm也对final做了优化,即保证fianl的可见性(猜测底层也是使用了内存屏障)。

4.总结

JMM实现共享变量的可见性的方式有两种,第一调用storeload方法加入内存屏障来实现可见性(针对x86架构),第二种方式就是上下文切换重新从共享内存加值新的值(yield方法)。

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

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

相关文章

基于springboot实现校园在线拍卖系统项目【项目源码】

基于springboot实现校园在线拍卖系统演示 Javar技术 JavaScript是一种网络脚本语言,广泛运用于web应用开发,可以用来添加网页的格式动态效果,该语言不用进行预编译就直接运行,可以直接嵌入HTML语言中,写成js语言&…

Modbus转Profinet改变局面,PLC与电力仪表秒级响应

Modbus转Profinet改变了传统的局面,实现了PLC与电力仪表之间的秒级响应。在过去,由于Modbus通信协议的限制,PLC与电力仪表之间的数据传输速度受到了很大的限制,无法满足工业自动化领域对实时性的要求。然而,随着Modbus…

【云原生 Prometheus篇】Prometheus架构详解与核心组件的应用实例(Exporters、Grafana...)

Prometheus Part1 一、常用的监控系统1.1 简介1.2 Prometheus和zabbix的区别 二、Prometheus2.1 简介2.2 Prometheus的主要组件1)Prometheus server2)Exporters3)Alertmanager4)Pushgateway5)Grafana 2.3 Prometheus的…

openGauss学习笔记-130 openGauss 数据库管理-参数设置-重设参数

文章目录 openGauss学习笔记-130 openGauss 数据库管理-参数设置-重设参数130.1 背景信息130.2 GUC参数设置130.3 操作步骤130.4 示例 openGauss学习笔记-130 openGauss 数据库管理-参数设置-重设参数 130.1 背景信息 openGauss提供了多种修改GUC参数的方法,用户可…

【网络】数据链路层协议

数据链路层协议 一、链路层解决的问题二、以太网协议1、局域网技术2、令牌环网(了解)3、以太网通信原理4、 MAC地址5、以太网帧格式6、碰撞避免7、最大传输单元MTU 二、ARP协议1、ARP数据的格式2、ARP协议的工作流程3、ARP缓存表4、ARP协议中的一些问题7…

11月23日星期四今日早报简报微语报早读

11月23日星期四,农历十月十一,早报微语早读。 1、我国5G基站总数达321.5万个; 2、2023年两院院士增选结果揭晓,共133人当选; 3、北京低保标准提升至每人每月1395元; 4、上海制定体育发展条例&#xff1a…

[Linux] shell脚本之循环

一、循环定义 一组被重复执行的语句称之为 循环体,能否继续重复,决定循环的终止条件。 循环语句 是由循环体及循环的终止条件两部分组成的。 二、for循环 2.1 带列表循环 语法 for 变量名 in 取值列表do 命令序列 done 花括号用法: 花括号{ }和seq在for循环…

年轻有为!2023两院院士增选揭榜 45岁颜宁当选

大家好,我是极智视界,欢迎关注我的公众号,获取我的更多前沿科技分享 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码和资源下载,链接:https://t.zsxq.com/0aiNxERDq 通常,两…

BTS-GAN:基于MRI和条件对抗性网络的乳腺肿瘤计算机辅助分割系统

BTS-GAN: Computer-aided segmentation system for breast tumor using MRI and conditional adversarial networks BTS-GAN:基于MRI和条件对抗性网络的乳腺肿瘤计算机辅助分割系统背景贡献实验方法Parallel dilated convolution module(并行扩展卷积模块…

逸学java【初级菜鸟篇】9.5枚举

hi,我是逸尘,一起学java吧 枚举是信息的标志和分类 当一个变量有几种固定可能的取值时,就可以将它定义为类型的枚举。 优点:代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术&#x…

【AI读论文】AutoML的8年回顾:分类、综述与趋势

论文标题:Eight years of AutoML: categorisation, review and trends 论文链接:https://link.springer.com/article/10.1007/s10115-023-01935-1 本文主要围绕自动机器学习(AutoML)展开了系统性的文献综述,总结了该领…

【文末送书】重磅!这本30w人都在看的Python数据分析畅销书:更新了!

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关…

div中添加el-loading(局部loading的使用)

效果&#xff1a;在div中实现el-loading <div class"content-main">{{ hotList }}</div>getHotList(columnType) {this.$nextTick(() > {var loading this.$loading({lock: true,text: "努力加载中...",spinner: "el-icon-loading&qu…

揭示卡尔曼滤波器的威力

一、说明 作为一名数据科学家&#xff0c;我们偶尔会遇到需要对趋势进行建模以预测未来值的情况。虽然人们倾向于关注基于统计或机器学习的算法&#xff0c;但我在这里提出一个不同的选择&#xff1a;卡尔曼滤波器&#xff08;KF&#xff09;。 1960 年代初期&#xff0c;Rudol…

开始通过 Amazon SageMaker JumpStart 在亚马逊云科技上使用生成式 AI

目前&#xff0c;生成式 AI 正受到公众的广泛关注&#xff0c;人们围绕着许多人工智能技术展开讨论。很多客户一直在询问有关亚马逊云科技生成式 AI 解决方案的更多信息&#xff0c;本文将为您进行解答。 这篇文章通过一个真实的客户使用案例概述了生成式 AI&#xff0c;提供了…

感恩节99句祝福语,感恩父母老师朋友亲人朋友们,永久快乐幸福

1、流星让夜空感动&#xff0c;生死让人生感动&#xff0c;爱情让生活感动&#xff0c;你让我感动&#xff0c;在感恩节真心祝福你比所有的人都开心快乐。 2、感恩节到了&#xff0c;想问候你一下&#xff0c;有太多的话语想要说&#xff0c;但是不知从何说起&#xff0c;还是用…

让SOLIDWORKS Composer动画在PPT中随意转换

SOLIDWORKS Composer作为一款易学易用的技术图解软件&#xff0c;非常适合用来给客户展示自己的产品。这里我们教大家如何将Composer文件插入大PPT中&#xff0c;并任意切换文件&#xff0c;用以给客户展示不用的方案和产品。 1.首先大家要安装SOLIDWORKS Composer Player 这个…

【2021集创赛】基于ARM-M3的双目立体视觉避障系统 SOC设计

本作品参与极术社区组织的有奖征集|秀出你的集创赛作品风采,免费电子产品等你拿~活动。 团队介绍 参赛单位&#xff1a;上海电力大学 队伍名称&#xff1a;骇行队 总决赛奖项&#xff1a;二等奖 1.摘要 随着信息技术的发展&#xff0c;AGV&#xff08;Automated Guided Vehic…

21款奔驰GLC260L升级HUD抬头显示 平视仪表信息

随着科技飞速地发展&#xff0c;从汽车领域就可以看出&#xff0c;尤其是汽车的抬头显示器&#xff0c;一经推出就吸引了很多的车主。 升级HUD抬头显示&#xff0c;HUD与汽车系统进行完整的数据信息连接&#xff0c;整合成大数据&#xff0c;然后将一些重要信息映射到车窗玻璃…

中低压MOSFET 2N7002W 60V 300mA 双N通道 SOT-323封装

2N7002W小电流双N通道MOSFET&#xff0c;电压60V电流300mA&#xff0c;采用SOT-323封装形式。超高密度电池设计&#xff0c;适用于极低的ros (on)&#xff0c;具有导通电阻和最大直流电流能力&#xff0c;ESD保护。可应用于笔记本中的电源管理&#xff0c;电池供电系统等产品应…