专业的JAVA编程教程与资源

网站首页 > java教程 正文

面试题之Java内存模型(java内存模型happens before)

temp10 2025-03-24 20:47:58 java教程 11 ℃ 0 评论

Java内存模型(Java Memory Model, JMM)是面试必问的题目,是被设计用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台上都能达到一致内存访问效果。

涉及题目:

面试题之Java内存模型(java内存模型happens before)

  1. 为什么要设计JMM?
  2. 什么是JMM?
  3. JMM的作用是什么?
  4. JMM的原理是什么?

为什么需要 JMM?

背景:因为程序的指令重排序可以在很大程度上提升程序的性能。Java 语言规范要求 JVM 在线程中维护一种类似串行的语义:只要程序的最终结果与在严格串行环境下执行的结果相同,这些操作都是被允许的,比如程序在编译器中的指令顺序可以与源代码中的顺序不同;处理器还可以乱序或并行等方式来执行指令等。

基本概念

  • JMM作用及目的:用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的内存访问效果。目的是定义在程序中各个变量的访问规则。
  • JMM定义:JMM规定了所有 的变量必须保存在主内存中。每条线程都有自己的工作内存。工作内存中保存了该线程使用到的变量的主内存副本拷贝。线程对变量的所有操作都必须 在工作内存中进行,而不能直接读写主内存中的变量。
  • JMM是围绕着在并发过程中如何解决变量的原子性、可见性和有序性这三个特征来建立的。
  • 原子性:由JMM来直接保证的原子性变量操作包括read、load、use、assign、store和write。可以认为基本数据类型的读写都是具备原子性的(除了float和double的非原子协定)。如果程序需要一个更大原子性的保证,JMM还提供了lock和unlock操作来满足这种需求。这个操作没有直接提供给用户,提供了更高层次的字节码指令monitorenter和 monitorexit,反映到java代码块就是synchronized关键字。因此,synchronized块之间操作也具备原子性。
  • 可见性:JMM是通过在变量修改后将新值同步回主内存,在变量读取 前从主内存刷新变量值这种依赖主内存的作为传递媒介的方式来实现可见性的。普通变量和volatile变量都是如此,区别就是volatile变量保证了新值能立即同步回主内存。扩展--除了volatile外,java还有两个关键字保证了可见性:synchronized和final。同步块的可见性: 是由对一个变量执行unlock操作之前必须先把此变量同步回主内存中这条规则获取的。final关键字的可见性是指:被修饰为final的字段在构造器中一旦完成初始化,并且没有被“this”的引用传递出去,那在其他的线程中就能看到final字段的值。(由哪个工作内存来修改主内存的值,是通过硬件的mesi等一致性协议来解决的)
  • 有序性:在一个线程内所有的操作都是有序的,也就是指线程内表现为串行的语义;在一个线程观察另外一个线程,所有的操作都是无序的,是指“指令重排序”现象和“工作内存与主内存同步延迟”现象。java语言提供volatile和synchronized两个关键字来保证线程间操作的有序性,volatile关键字本身就包含了禁止指令重排序的语义,而synchronized则是由“一个变量在同一时刻只允许一条线程对其进行lock操作”这条规则获取的。除这两个关键字之外,java还提供了“先行发生原则”来完成jmm中所有的有序性。JMM 为程序中的所有操作定义了一种偏序关系,称之为 Happens-before。要想保证执行操作 B 的线程看到操作 A 的结果(无论 A、B是否在同一个线程中执行),那么在 A 和 B 之间必须满足 Happens-before 关系。如果两个操作之间缺少 Happens-before 关系,那么 JVM 可以对它们任意地重排序。(程序次序规则、管程锁定规则、volatile变量规则、线程启动-终止-中断规则)

有序性实现原理:

  • 内存屏障:Java 内存模型是通过内存屏障(memory barrier)来禁止重排序的。对于即时编译器来说,它会针对每一个 happens-before 关系,向正在编译的目标方法中插入相应的读读、读写、写读以及写写内存屏障。这些内存屏障会限制即时编译器的重排序操作。
  • 依赖底层体系架构:即时编译器将根据具体的底层体系架构,将这些内存屏障替换成具体的 CPU 指令。以我们日常接触的 X86_64 架构来说,读读、读写以及写写内存屏障是空操作(no-op),只有写读内存屏障会被替换成具体指令[2]。

todo:

  1. JMM实现与硬件CPU、高速缓存与主内存之间的类比?
  2. 为什么乱序执行会提高效率,如何提高的?
  3. happen-before先行发生原则具体指什么?

思考:

  1. 硬件设计与软件设计的类比、关联,如何借鉴?
  2. 从问题本身思考解决方案是不是更容易理解jmm的设计意图?

参考:

《Java并发实践》

《深入理解Java虚拟机》

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表