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

[复制链接]
发表于 2026-1-18 08:05:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
阿华代码,不是逆风,就是我疯,你们的点赞收藏是我进步最大的动力!!盼望本文内容可以或许资助到你!
目次

一:内存可见性题目
1:代码表明
2:结果分析
(1)指令拆解
①load
②访问寄存器
(2)指令分析
3:JVM代码优化
4:办理题目
(1)引入.sleep()
(2)volatile
(3)正确形貌


一:内存可见性题目

内存可见性引起的多线程安全题目(一个线程读,一个线程写)
  1. package thread;
  2. import java.util.Scanner;
  3. /**
  4. * Created with IntelliJ IDEA.
  5. * Description:
  6. * User: Hua YY
  7. * Date: 2024-09-23
  8. * Time: 10:50
  9. */
  10. public class ThreadDemon26 {
  11.     public static int flag = 0;
  12.     public static void main(String[] args) {
  13.         Thread t1 = new Thread(()->{
  14.             while(flag == 0){//等待t1线程输入flag的值,只要不为0就能结束t1线程
  15.             }
  16.             System.out.println("t1线程结束");
  17.         });
  18.         Thread t2 = new Thread(()->{
  19.             System.out.println("请输入flag的值");
  20.             Scanner scanner = new Scanner(System.in);
  21.             flag = scanner.nextInt();
  22.         });
  23.         t1.start();
  24.         t2.start();
  25.     }
  26. }
复制代码
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模子当中,“主内存”对标内存,“工作内存”对标寄存器+缓存哪一套,之以是这么叫是由于方便跨平台利用。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表