【JavaEE】——内存可见性问题

server/2024/10/19 9:32:42/

阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!希望本文内容能够帮助到你!

目录

一:内存可见性问题

1:代码解释

2:结果分析

(1)指令拆解

①load

②访问寄存器

(2)指令分析

3:JVM代码优化

4:解决问题

(1)引入.sleep()

(2)volatile

(3)准确描述


一:内存可见性问题

内存可见性引起的多线程安全问题(一个线程读,一个线程写)

package thread;import java.util.Scanner;/*** Created with IntelliJ IDEA.* Description:* User: Hua YY* Date: 2024-09-23* Time: 10:50*/
public class ThreadDemon26 {public static int flag = 0;public static void main(String[] args) {Thread t1 = new Thread(()->{while(flag == 0){//等待t1线程输入flag的值,只要不为0就能结束t1线程}System.out.println("t1线程结束");});Thread t2 = new Thread(()->{System.out.println("请输入flag的值");Scanner scanner = new Scanner(System.in);flag = scanner.nextInt();});t1.start();t2.start();}
}

1:代码解释

这段代码想要表现出来的效果是,t1,t2线程同时运行,通过t2线程中输入的flag的值来控制t1线程是否结束。

例如:t2线程给flag赋值,输入一个1,那么此时t1线程就不会进入while循环,打印t1线程结束。输入0,那t1线程就陷入死循环

2:结果分析

上文我们先后输入了1,0,2......都没能使t1线程结束,这是为什么呢?

(1)指令拆解

while(flag == 0){};

这条语句其实有两个指令

①load

cpu从内存中读取flag的值(load)到cpu的寄存器上(开销很大)

②访问寄存器

cpu访问寄存器中存储的flag的值,与0进行比较(条件跳转指令)(开销低)         

(此处不理解load和为什么开销很大,请看阿华写的前面的文章哈,有详细解释) 

(2)指令分析

重点条件:①中load的操作(读内存),相较于②中访问寄存器的操作,开销大的多。

上述while循环中①②这两条指令整体看,执行的速度非常快,等你scanner几秒钟了,我while循环中①②可能都执行几亿次了(cpu的计算能力非常强)

此时JVM就会怀疑,这个①号load 的操作是否还有存在的必要(节省开销),前几次可能还会load一下,后面发现,反正load 的值都一样(速度太快了,等不到我们scanner输入flag的值),索性就把load这个操作给优化掉,只留一个访问寄存器的操作指令,访问之前寄存器中“缓存”的值,大大提高循环的执行速度。

3:JVM代码优化

在我们编译完代码后,JVM会在保持你代码逻辑不变的前提下,对你写过的代码进行智能分析,并进行优化。

这个保持你代码逻辑不变的条件其实很苛刻,单线程还好,但是遇到多线程就难免会遇到一些bug。

我们上述的代码就是t2修改了内存,但是t1并没有看到,这就叫“内存可见性问题”

4:解决问题

(1)引入.sleep()

治标不治本,加入sleep,load的循环次数减少,JVM优化的迫切程度就会降低

(2)volatile

volatile关键字,是强制性关闭优化,保证每次循环都会从内存中读取数据。开销是变大了,但是数据更准了

功能①:保证内存可见性,每次访问变量都要读取内存,而不是优化到寄存器或者缓存器当中

功能②:禁止指令重排序,对于被volatile修饰的变量的操作指令,是不能被重排序的

(3)JMM模型准确描述

我们的描述:在上述代码中,编译器发现,每次循环都要读取读取内存,开销太大,于是就把读取内存操优化为读取寄存器操作。

JMM模型描述:在上述代码中,编译器发现,每次循环都要读取“主内存”,开销太大,于是就把“主内存”中的数据拷贝到“工作内存”中,后续每次读取都是到“工作内存”中。

注:在JMM模型当中,“主内存”对标内存,“工作内存”对标寄存器+缓存哪一套,之所以这么叫是因为方便跨平台使用。


http://www.ppmy.cn/server/123096.html

相关文章

多线程事务管理:Spring Boot 实现全局事务回滚

多线程事务管理:Spring Boot 实现全局事务回滚 在日常开发中,我们常常会遇到需要在多线程环境下进行数据库操作的场景。这类操作的挑战在于如何保证多个线程中的数据库操作要么一起成功,要么一起失败,即 事务的原子性。尤其是在多个线程并发执行的情况下,确保事务的一致性…

速盾:cdn是怎么加速视频的?

CDN(Content Delivery Network)是一种网络加速服务,通过将内容分发到全球各地的服务器节点上,提供更快速度和更可靠的内容传输。当涉及到视频内容时,CDN起到了至关重要的作用,它通过一系列的技术和策略来加…

IDEA2020运行项目时不从配置的maven仓库找jar包,从C盘默认路径下找jar包

目录 问题描述: 解决方案: 问题描述: 使用IDEA2020做java开发,idea的设置中maven仓库地址配在D盘, maven的配置文件setting.xml中的仓库也已经确认配置到D盘, 项目根据pom文件自动下载jar包时也会下载到…

网络安全入门教程(非常详细)从零基础入门到精通,看完这一篇就够了。

学前感言: 1.这是一条坚持的道路,三分钟的热情可以放弃往下看了.2.多练多想,不要离开了教程什么都不会了.最好看完教程自己独立完成技术方面的开发.3.有时多google,baidu,我们往往都遇不到好心的大神,谁会无聊天天给你做解答.4.遇到实在搞不懂的,可以先放放,以后再来解决. 基…

批量发送邮件:性能优化与错误处理深度解析

目录 一、批量发送邮件的基础概述 1.1 批量发送邮件的定义 1.2 邮件发送流程 二、性能优化策略 2.1 发送速率控制 2.2 队列管理 2.3 动态IP池管理 2.4 智能调度 三、错误处理机制 3.1 暂时性发送错误处理 3.2 永久性发送错误处理 3.3 邮件反馈收集与分析 四、案例…

[半导体检测-8]:KLA Surfscan 系统设备组成

目录 一、概述 1. 核心技术与结构 2. 关键组件 3. 应用领域与特性 4. 具体型号示例 二、KLA Surfscan DUV激光源 2.1 概述 1. 技术特点 2. 应用优势 3. 系统集成 4. 实际应用案例 2.2 KLA Surfscan DUV激光源的技术指标 KLA Surfscan系统设备作为KLA公司专注于无图…

Android界面控件概述

节选自《Android应用开发项目式教程》,机械工业出版社,2024年7月出版 做最简单的安卓入门教程,手把手视频、代码、答疑全配齐 控件是Android界面的重要组成单元,Android应用主要通过控件与用户交互,Android提供了非常…

一文了解:最新版本 Llama 3.2

Meta AI最近发布了 Llama 3.2。这是他们第一次推出可以同时处理文字和图片的多模态模型。这个版本主要关注两个方面: 视觉功能:他们现在有了能处理图片的模型,参数量从11亿到90亿不等。 轻量级模型:这些模型参数量在1亿到3亿之间…