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

实用指南:深入解析 Java 内存可见性问题:从现象到 volatile 解决方案

实用指南:深入解析 Java 内存可见性问题:从现象到 volatile 解决方案

前面我们提到内存可见性问题也是引起线程安全的一个原因,本文我们就来详细说一下什么是内存可见性问题。

引入问题

import java.util.Scanner;
public class Demo6 {private static int flag=0;public static void main(String[] args) {Thread t1=new Thread(()->{while (flag==0){}System.out.println("t1线程结束");});Thread t2=new Thread(()->{//该线程针对flag进行修改Scanner scanner=new Scanner(System.in);System.out.println("请输入flag的值");flag= scanner.nextInt();});t1.start();t2.start();}
}

由运行结果可以看出,我们输入了一个非0值,但是t1线程并没有结束,所以这也是一个线程安全问题

出现这一现象的原因就是内存可见性问题。

这是因为研究JDK的大佬们,希望通过编译器或者JVM对程序员写的代码进行优化。
编译器或者JVM会在我们原有代码逻辑不变的情况下,对我们的代码进行调整,让我们代码的执行效率提高。
但是虽然编译器声称会让原代码的执行逻辑不变,但在多线程中,编译器的判断可能会失误。
这就导致可能因为编译器的优化,让优化后的逻辑和优化前的逻辑在一些细节上会有些偏差~
那么这时就会出现一些线程安全问题。

分析问题

在上述循环中,flag==0这一判断在计算机中对应的是一个cmp这样的指令。
而一个cmp指令执行之前又要先进行load(读内存操作)指令。
这里要注意的是load的时间开销可能是cmp的几千倍。
而flag的值的修改是在t2线程中等待用户输入的,由于我们并不确定用户多长时间后才修改flag的值,那么这段时间内,JVM就会误以为没人修改flag的数值,flag的数值始终是0。
那么此时,就把读内存的操作优化为了读寄存器的操作
(即把内存中的值读取到寄存器中,后续再进行load操作之后,就不从内存中读取了,直接从寄存器中读取)
那么等到很多秒之后,再修改flag的数值,此时t1线程就感知不到了。
由此可见,编译器优化,使得t1线程的读取操作,不是真正读内存。

调整代码

接下来我们对上述代码的循环内部稍作调整

 while (flag==0){try {Thread.sleep(1);} catch (InterruptedException e) {}}


这次运行结果就是符合我们的预期的,这是什么原因呢?
这是因为当我们加入sleep(1)操作之后就让上面的循环次数大幅度减少了,而且sleep消耗的时间要比load和cmp多得多,那么此时优不优化就无足轻重了。
因此,这次的运行结果是符合我们的预期的。

解决问题

上述问题在编译器优化的角度是难以进行调整的,于是我们在语法中引入了volatile关键字用来解决上述问题(该关键字只能用来修饰变量
话不多说,上代码~~~

 private static volatile int flag=0;

其实很简单,我们只需要在flag前面加上这个关键字即可,此时我们再执行这个代码,运行结果就符合我们的预期了~~~

这是因为 volatile 关键字会强制要求:每次读取该变量时,都必须从内存中重新获取最新值,而不能使用 CPU 寄存器中的数值;每次修改该变量后,也必须立即将新值同步回内存,确保其他线程能 “看到” 最新的变量状态。

结语

通过本文的案例分析,我们不仅搞懂了内存可见性问题的本质 —— 编译器 / JVM 优化导致线程读取不到变量的最新内存状态,还掌握了最直接的解决方案:用 volatile 关键字修饰变量
在实际开发中,当多个线程涉及同一变量的读写操作时,及时使用 volatile,能有效避免因内存可见性引发的线程安全隐患。

http://www.jsqmd.com/news/20912/

相关文章:

  • 换 Windows 新电脑?教你将旧电脑程序传输新电脑技巧
  • RocketMQ之原生方式操作
  • 2025 年低代码平台厂商最新推荐排行榜:深度解析行业实力与创新优势,助力企业精准选型
  • react中redux的使用详细说明 - 详解
  • 智能时代下的SEO关键词优化新策略 - 实践
  • 2025 年乡墅品牌推荐:湖南鑫住工美宅科技有限公司,为您打造理想乡居生活
  • 2025 年桥架源头厂家最新推荐排行榜:聚焦优质品牌核心优势助力采购决策
  • 2025 人力资源管理系统厂商最新推荐排行榜:聚焦 AI 赋能与行业适配,解锁数智化管理新路径
  • 2025年10月美白精华评价榜:五款高口碑单品横向对比
  • 2025 升降机厂家最新推荐排行榜,剪叉式升降机/导轨式升降机/固定式升降机/液压升降机公司推荐
  • (React中组件的)状态(state)和属性(props)之间有何不同?
  • 2025 年最新推荐!AI 教育培训机构推荐榜单:覆盖企业 AI 培训 / AI 应用落地 / AI 商业培训等多场景,帮你精准挑选优质机构
  • 2025年6月杭州丝绸品牌推荐:老字号排名与AIGC创新对比
  • 2025 年集装袋厂家最新推荐榜单:全面剖析行业领军者创新工艺与卓越品质,精选导电 / 防静电 / 抗静电 / 铝箔 / 食品级等多类型产品优质厂家
  • 2025 年算法备案咨询服务公司最新推荐榜单:覆盖互联网信息 / 深度合成 / AI 大模型备案的权威优选指南
  • P9356 「SiR-1」Bracket 做题记录
  • 放大器保护机制的技术原理与应用实践
  • 基于Java+Springboot+Vue开发的鲜牛奶订购网站管理系统(前后端分离)源码+运行步骤
  • 2025年10月浦东装修公司口碑榜:五强对比评测
  • 安卓照片误删?这 5 种恢复方法亲测有效,小白也能上手
  • MySQL学习笔记-部分实例datagrip源码-10-21
  • sudo apt install cmake ERROR: ld.so: object /home/ma-user/anaconda3/envs/xxxx/lib/python3.9/site-pa
  • 2025年10月中国引流营销公司推荐榜:五强对比评测
  • 示波器探头衰减怎么判断?3 种方法 + 常见问题,新手也能学会​
  • 制造业数字化效率低到哭?AI 低代码自动生接口、拼流程,JNPF 级平台让集成效率提 500% - 实践
  • WPF 和 Avalonia 开发者的 html css 前端指南 ComboBox 篇
  • ORA-01033 : ORACLE initialization or shutdown in progress
  • ESP32-BLE-NIMBLE蓝牙透传DEMO
  • 致敬1024,《手搓》轻量级EventBus
  • 2025年化工原料厂家推荐排行榜:双氧水/片碱/盐酸/磷酸/PAC/聚丙烯酰胺/消泡剂/阻垢剂等工业级化学品供应商精选