当前位置: 首页 > news >正文

线程可见性问题?还是编译优化问题?

代码还原

首先我们看下下面的代码

public class VolatileTest {public static int count = 1;public static void setCount() {while (count == 1 ) {}System.out.println("setCount 方法执行");}public static void main(String[] args) throws InterruptedException {new Thread(() -> setCount()).start();Thread.sleep(100);count = 2;}
}

上面代码的逻辑非常简单,这里就不在多赘述了。
使用JVM1.8 虚拟机参数是默认参数,代码执行之后,发现程序结束不了

问题分析观点

1: 线程间可见性问题

有人说是线程可见性问题。变量count的值被修改了,子线程执行setCount()方法的时候,count的值一直不可见的。给count加上volatile关键字就能解决问题。代码如下

public class VolatileTest {public static volatile int count = 1;public static void setCount() {while (count == 1 ) {}System.out.println("setCount 方法执行");}public static void main(String[] args) throws InterruptedException {new Thread(() -> setCount()).start();Thread.sleep(100);count = 2;}
}

执行完成之后,你会发现,问题确实解决了。

但这真的是线程可见性的问题吗?

我们思考一下,count的值是类成员变量,一个线程修改count值之后,肯定会刷入到内存中的。那也就是说,在不加volatile的前提下,如果我把代码中 Thread.sleep()的方法参数中的时间调大一些,应该能够将count的最新值刷入到内存中,setCount()方法可见。试了之后,发现无论调多大时间,程序都终止不了。

2:编译器优化问题

我们知道,JVM会对我们的代码进行编译优化,我们来验证一下,是不是编译器优化的问题。

JVM采用分层编译, JVM 将执行状态分成了 5 个层次:
0层:解释执行,用解释器将字节码翻译为机器码
1层:使用 C1 即时编译器编译执行(不带 profiling)
2层:使用 C1 即时编译器编译执行(带基本的profiling)
3层:使用 C1 即时编译器编译执行(带完全的profiling)
4层:使用 C2 即时编译器编译执行。

也就是说,只需要验证一下C1和C2编译之后执行的代码是否有问题即可

情景1: 只用C1编译执行,禁止C2编译执行

JVM参数新增 -XX:TieredStopAtLevel=3 此参数的作用是仅启用C1编译器,同时禁用C2编译器
执行之后,你会发现,不加volatile关键字,程序也能结束。

情景2: 只用C2编译执行,禁止C1编译执行

JVM参数新增 -XX:TieredStopAtLevel=4 此参数JVM将使用C2编译器进行编译。
执行之后,你会发现,程序结束不了。

结论

导致代码执行问题的根本问题是 编译器优化问题,并不是线程间的可见性问题。这里这是证明了问题发生在C2编译器编译的地方。实际上我们可以看下JVM执行的C2编译之后指令即可发现问题的根本所在。这里有兴趣的读者可以本地编译一下JVM源码,将执行的指令打印出来,进行日志分析即可。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • golang fmt.Printf中 %q
  • 【React】Vite 构建 React
  • Zustand 状态调试以及持久话
  • SpringBoot与Minio的极速之旅:解锁文件切片上传新境界
  • pytorch torch.matmul函数介绍
  • 基于SpringBoot的宠物服务系统+uniapp小程序+LW参考示例
  • 【redis】redis的特性和主要应用场景
  • 码上君量化互助社群介绍
  • javascript嵌套循环
  • 使用shell脚本安装mysql8,进行主从备份配置
  • route-forward springboot实现路由转发程序
  • 系统架构师-ERP+集成
  • lwip移植-基于类rtosw5500
  • 实例:如何统计当前主机的连接状态和连接数
  • 实习项目|苍穹外卖|day8
  • 深入了解以太坊
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 2017年终总结、随想
  • docker python 配置
  • idea + plantuml 画流程图
  • JavaScript类型识别
  • Map集合、散列表、红黑树介绍
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • quasar-framework cnodejs社区
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 浅谈web中前端模板引擎的使用
  • 如何学习JavaEE,项目又该如何做?
  • -- 数据结构 顺序表 --Java
  • 通过git安装npm私有模块
  • 找一份好的前端工作,起点很重要
  • - 转 Ext2.0 form使用实例
  • 阿里云API、SDK和CLI应用实践方案
  • 容器镜像
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • ​力扣解法汇总946-验证栈序列
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #pragma预处理命令
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (2024,RWKV-5/6,RNN,矩阵值注意力状态,数据依赖线性插值,LoRA,多语言分词器)Eagle 和 Finch
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (LeetCode C++)盛最多水的容器
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (一)基于IDEA的JAVA基础12
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转)德国人的记事本
  • .apk 成为历史!
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NET 药厂业务系统 CPU爆高分析