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

JVM_JMM

一、JMM 是什么?

核心定义

        JMM 是一套规范,它定义了Java程序中各种变量(线程共享变量)的访问方式,以及在并发环境下如何保证数据的可见性有序性原子性

关键点:

  • 它不是物理内存模型:JMM并不真实存在,它只是一个抽象的规则和约定,描述的是线程和主内存之间的抽象关系。

  • 它是一种规范:它规定了JVM如何与计算机内存协同工作,特别是在多线程环境下。

  • 它的目标:屏蔽掉各种硬件和操作系统的内存访问差异,让Java程序在各种平台下都能达到一致的内存访问效果,从而保证并发程序的正确性。

你可以把它想象成 Java并发世界的“宪法”,它规定了线程之间如何通信、如何共享数据,所有JVM实现(如HotSpot、J9等)都必须遵守这套规范。


二、为什么需要 JMM?—— 解决的问题

计算机硬件(CPU、内存)为了提升性能,做出了一些优化,但这些优化会带来一些问题:

  1. CPU高速缓存 vs. 可见性问题

    • 现象:现代CPU都有多级缓存(L1, L2, L3)。线程运行时,会将主内存中的数据拷贝一份到自己的工作内存(CPU缓存)中操作。操作完成后,再写回主内存。

    • 问题:如果一个线程修改了自己工作内存中的副本,但没有及时写回主内存,或者其他线程没有及时从主内存刷新最新值,就会导致其它线程看不到最新的修改。这就是可见性问题

  2. 指令重排序 vs. 有序性问题

    • 现象:为了充分利用CPU资源,编译器和处理器可能会对指令的执行顺序进行重新排序(在不改变单线程程序执行结果的前提下)。

    • 问题:在多线程环境下,这种重排序可能会破坏程序的逻辑,导致结果出乎意料。这就是有序性问题

JMM就是为了解决这些由硬件优化带来的可见性和有序性问题而存在的

它通过定义一些关键的关键字(如 volatilesynchronized)和 Happens-Before 规则,为开发者提供了一套可靠的多线程内存访问机制


三、JMM 的核心结构:主内存与工作内存

JMM从抽象角度将内存划分为两类:

内存描述对应物理实体
主内存 (Main Memory)存储所有共享变量(实例字段、静态字段等)。所有线程都能访问。物理内存(RAM)的一部分
工作内存 (Working Memory)每个线程私有的内存空间。存储该线程使用到的变量的主内存副本。线程对变量的所有操作(读、写)都必须在工作内存中进行,不能直接读写主内存。CPU寄存器、高速缓存(L1, L2 Cache)

交互流程(简化版):

  1. Read(读取):线程从主内存读取共享变量到自己的工作内存。

  2. Load(加载):将read操作得到的值放入工作内存的变量副本中。

  3. Use(使用):线程执行引擎使用工作内存中的变量值。

  4. Assign(赋值):线程将一个新值赋给工作内存中的变量副本。

  5. Store(存储):将工作内存中改变后的变量值传递到主内存。

  6. Write(写入):将store操作带来的值放入主内存的变量中。

这个过程图示如下:

正是因为这个复杂的交互过程,如果缺乏同步,就会导致数据不一致。


四、JMM 解决的三大问题

JMM 主要围绕解决以下三个核心问题来设计:

1. 原子性 (Atomicity)

  • 问题:一个或多个操作要么全部执行成功,要么全部不执行,中间不能被中断。

  • JMM保障:JMM直接保证了基本数据类型的访问、读写是原子性的(longdouble非原子性协定,但商用JVM都实现了其原子性)。对于更大范围的代码块,可以通过 synchronized 关键字或 Lock 接口来保证其原子性。

2. 可见性 (Visibility)

  • 问题:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。

  • JMM保障

    • volatile 关键字:保证修改的值能立即被更新到主内存,并使其他线程中该变量的缓存行失效,从而强制其他线程必须从主内存重新读取最新值。

    • synchronized 关键字:在解锁前,必须将工作内存中的变量同步到主内存(执行store-write)。

    • final 关键字:被 final 修饰的字段在构造完成后就是可见的(前提是没有this引用逸出)。

3. 有序性 (Ordering)

  • 问题:程序执行的顺序按照代码的先后顺序执行。禁止指令重排序。

  • JMM保障

    • volatile 关键字:通过插入内存屏障(Memory Barrier)来禁止指令重排序。

    • synchronized 关键字:规定一个变量在同一个时刻只允许一条线程对其进行lock操作,这使得持有同一个锁的两个同步块只能串行进入,从而保证了有序性。

    • Happens-Before 原则:JMM最核心的规则,天然保证了有序性。


五、Happens-Before 原则

这是JMM的精髓。它定义了哪些操作在内存层面是“可见”的,即前一个操作的结果对后续操作是可见的。无需任何同步手段,如果操作A Happens-Before 操作B,那么A所做的任何操作对B都是可见的。

常见规则:

  1. 程序次序规则:在一个线程内,书写在前面的操作先行发生于后面的操作。

  2. 管程锁定规则:一个unlock操作先行发生于后面对同一个锁lock操作。

  3. volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。

  4. 线程启动规则Thread对象的start()方法先行发生于此线程的每一个动作。

  5. 线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测(如Thread.join())。

  6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。

  7. 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。

  8. 传递性:如果A Happens-Before B,且B Happens-Before C,那么A Happens-Before C。


总结

方面解释
JMM是什么Java内存模型,一套规范,定义了多线程下如何访问共享变量。
目的解决由于硬件优化(缓存、重排序)导致的可见性有序性问题,提供跨平台的内存访问一致性。
核心结构主内存(共享)和工作内存(线程私有)。
关键保障原子性synchronized)、可见性volatilesynchronized)、有序性volatilesynchronizedHappens-Before)。
重要性不理解JMM,就无法真正理解 synchronizedvolatile 和 java.util.concurrent 包的工作原理。

简单来说,JMM是Java并发编程的基石,它定义了线程如何与内存交互的规则,确保了多线程程序在不同平台上的正确性和可预测性。

http://www.xdnf.cn/news/1387585.html

相关文章:

  • 面试八股文之——JVM与并发编程/多线程
  • Python Imaging Library (PIL) 全面指南:PIL基础入门-构建简易图像编辑器
  • LSTM实战:回归 - 实现交通流预测
  • 在Windows系统上将Java的.jar文件部署为服务
  • stylelint在项目中使用
  • 构筑沉浸式3D世界:渲染、资源与体验的协同之道
  • 牛客网 DP35 二维前缀和
  • 【算法】链表专题
  • C#连接SQL-Server数据库超详细讲解以及防SQL注入
  • 零基础json入门教程(基于vscode的json配置文件)
  • 序列化和反序列的学习
  • 医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(五)
  • Word - Word 查找文本中的特定内容
  • Redis vs Elasticsearch:核心区别深度解析
  • c++二叉搜索树
  • 在Linux的环境下安装GitLab(保姆级别)
  • Ubuntu下的压缩及解压缩
  • Llama-index学习文档
  • AI驱动万物智联:IOTE 2025深圳展呈现无线通信×智能传感×AI主控技术融合
  • 【Python办公】CSV按列去重工具
  • LangChain实战(三):深入理解Model I/O - Prompts模板
  • 聊聊Prompt Engineering (提示词工程)
  • Rust Web框架Axum学习指南之响应和异常封装
  • websocket建立连接过程
  • AI供应链优化+AI门店排班:蜜雪冰城降本20%、瑞幸提效的AI商业落地实战
  • 港科大开放世界长时域具身导航!LOVON:足式机器人开放词汇目标导航
  • LeetCode Hot 100 Python (1~10)
  • 1 分钟 Maya 动画渲染要多久?5 天还是 5 小时
  • linux系统学习(15.启动管理)
  • 第一百零二章:AI的“未来电影制片厂CEO”:多模态系统落地项目实战(完整 AI 视频创作平台)