精华内容
下载资源
问答
  • 什么是Java内存模型

    2021-01-23 17:39:33
    Java 作为一种面向对象的语言,有很多概念,从名称上看起来比较相似,比如 JVM 内存结构、Java 内存模型,这两个截然不同的概念。 JVM 内存结构和 Java 虚拟机的运行时区域有关; Java 内存模型Java 的并发...

    容易混淆:JVM 内存结构 VS Java 内存模型

    Java 作为一种面向对象的语言,有很多概念,从名称上看起来比较相似,比如 JVM 内存结构、Java 内存模型,这是两个截然不同的概念。

    • JVM 内存结构和 Java 虚拟机的运行时区域有关;
    • Java 内存模型和 Java 的并发编程有关。

    JVM 内存结构(就是堆、虚拟机栈、方法区、程序计数器、本地方法栈等几个类别)

    从 Java 代码到 CPU 指令

    看完了 JVM 内存结构,回到 Java 内存模型上来。编写的 Java 代码,最终还是要转化为 CPU 指令才能执行的。

    为了理解 Java 内存模型的作用,首先就来回顾一下从 Java 代码到最终执行的 CPU 指令的大致流程:

    • 最开始,我们编写的 Java 代码,是 *.java 文件;
    • 在编译(包含词法分析、语义分析等步骤)后,在刚才的 *.java 文件之外,会多出一个新的 Java 字节码文件(*.class);
    • JVM 会分析刚才生成的字节码文件(*.class),并根据平台等因素,把字节码文件转化为具体平台上的机器指令
    • 机器指令则可以直接在 CPU 上运行,也就是最终的程序执行。

    为什么需要 JMM(Java Memory Model,Java 内存模型)

    • 在更早期的语言中,其实是不存在内存模型的概念的。
    • 所以程序最终执行的效果会依赖于具体的处理器,而不同的处理器的规则又不一样,不同的处理器之间可能差异很大,因此同样的一段代码,可能在处理器 A 上运行正常,而在处理器 B 上运行的结果却不一致。
    • 同理,在没有 JMM 之前,不同的 JVM 的实现,也会带来不一样的“翻译”结果。
    • 所以 Java 非常需要一个标准,来让 Java 开发者、编译器工程师和 JVM 工程师能够达成一致。
    • 达成一致后,我们就可以很清楚的知道什么样的代码最终可以达到什么样的运行效果,让多线程运行结果可以预期
    • 这个标准就是 JMM,这就是需要 JMM 的原因。

    JMM 是什么

    JMM规范

    • JMM 是和多线程相关的一组规范,需要各个 JVM 的实现来遵守 JMM 规范,以便于开发者可以利用这些规范,更方便地开发多线程程序。
    • 这样一来,即便同一个程序在不同的虚拟机上运行,得到的程序结果也是一致的。
    • 如果没有 JMM 内存模型来规范,那么很可能在经过了不同 JVM 的“翻译”之后,导致在不同的虚拟机上运行的结果不一样,那是很大的问题。
    • 因此,JMM 与处理器、缓存、并发、编译器有关。它解决了 CPU 多级缓存、处理器优化、指令重排等导致的结果不可预期的问题。

    JMM 是工具类和关键字的原理

    • 各种同步工具和关键字,包括 volatile、synchronized、Lock 等,其实它们的原理都涉及 JMM。
    • 正是 JMM 的参与和帮忙,才让各个同步工具和关键字能够发挥作用,帮我们开发出并发安全的程序。
    • 比如写了关键字 synchronized,JVM 就会在 JMM 的规则下,“翻译”出合适的指令,包括限制指令之间的顺序,以便在即使发生了重排序的情况下,也能保证必要的“可见性”
    • 这样一来,不同的 JVM 对于相同的代码的执行结果就变得可预期了
    • Java 程序员就只需要用同步工具和关键字就可以开发出正确的并发程序了,这都要感谢 JMM。
    展开全文
  • 什么是JAVA内存模型

    2019-08-09 18:58:15
    有一个关于JVM名词定义的问题,说”JVM内存模型“,有人会说是关于JVM内存分布(堆栈,方法区等)这些介绍,也有地方说(深入理解JVM虚拟机)上说Java内存模型是JVM的抽象模型(主内存,本地内存)。这两个到底怎么...

    尝试一句话概括java内存模型:保证多线程之间操作共享变量的正确性。

    synchronization  final  volatile

    在知识星球中,有个小伙伴提了一个问题:
    有一个关于JVM名词定义的问题,说”JVM内存模型“,有人会说是关于JVM内存分布(堆栈,方法区等)这些介绍,也有地方说(深入理解JVM虚拟机)上说Java内存模型是JVM的抽象模型(主内存,本地内存)。这两个到底怎么区分啊?有必然关系吗?比如主内存就是堆,本地内存就是栈,这种说法对吗?

    时间久了,我也把内存模型和内存结构给搞混了,所以抽了时间把JSR133规范中关于内存模型的部分重新看了下。

    后来听了好多人反馈:在面试的时候,有面试官会让你解释一下Java的内存模型,有些人解释对了,结果面试官说不对,应该是堆啊、栈啊、方法区什么的(这不是半吊子面试么,自己概念都不清楚)

    JVM中的堆啊、栈啊、方法区什么的,是Java虚拟机的内存结构,Java程序启动后,会初始化这些内存的数据。

    内存结构就是上图中内存空间这些东西,而Java内存模型,完全是另外的一个东西。

    什么是内存模型

    在多CPU的系统中,每个CPU都有多级缓存,一般分为L1、L2、L3缓存,因为这些缓存的存在,提供了数据的访问性能,也减轻了数据总线上数据传输的压力,同时也带来了很多新的挑战,比如两个CPU同时去操作同一个内存地址,会发生什么?在什么条件下,它们可以看到相同的结果?这些都是需要解决的。

    所以在CPU的层面,内存模型定义了一个充分必要条件,保证其它CPU的写入动作对该CPU是可见的,而且该CPU的写入动作对其它CPU也是可见的,那这种可见性,应该如何实现呢?

    有些处理器提供了强内存模型,所有CPU在任何时候都能看到内存中任意位置相同的值,这种完全是硬件提供的支持。

    其它处理器,提供了弱内存模型,需要执行一些特殊指令(就是经常看到或者听到的,memory barriers内存屏障),刷新CPU缓存的数据到内存中,保证这个写操作能够被其它CPU可见,或者将CPU缓存的数据设置为无效状态,保证其它CPU的写操作对本CPU可见。通常这些内存屏障的行为由底层实现,对于上层语言的程序员来说是透明的(不需要太关心具体的内存屏障如何实现)。

    前面说到的内存屏障,除了实现CPU之前的数据可见性之外,还有一个重要的职责,可以禁止指令的重排序。

    这里说的重排序可以发生在好几个地方:编译器、运行时、JIT等,比如编译器会觉得把一个变量的写操作放在最后会更有效率,编译后,这个指令就在最后了(前提是只要不改变程序的语义,编译器、执行器就可以这样自由的随意优化),一旦编译器对某个变量的写操作进行优化(放到最后),那么在执行之前,另一个线程将不会看到这个执行结果。

    当然了,写入动作可能被移到后面,那也有可能被挪到了前面,这样的“优化”有什么影响呢?这种情况下,其它线程可能会在程序实现“发生”之前,看到这个写入动作(这里怎么理解,指令已经执行了,但是在代码层面还没执行到)。通过内存屏障的功能,我们可以禁止一些不必要、或者会带来负面影响的重排序优化,在内存模型的范围内,实现更高的性能,同时保证程序的正确性。

    下面看一个重排序的例子:

    Class Reordering {
      int x = 0, y = 0;
      public void writer() {
        x = 1;
        y = 2;
      }
    
      public void reader() {
        int r1 = y;
        int r2 = x;
      }
    }
    

    假设这段代码有2个线程并发执行,线程A执行writer方法,线程B执行reader方法,线程B看到y的值为2,因为把y设置成2发生在变量x的写入之后(代码层面),所以能断定线程B这时看到的x就是1吗?

    当然不行! 因为在writer方法中,可能发生了重排序,y的写入动作可能发在x写入之前,这种情况下,线程B就有可能看到x的值还是0。

    在Java内存模型中,描述了在多线程代码中,哪些行为是正确的、合法的,以及多线程之间如何进行通信,代码中变量的读写行为如何反应到内存、CPU缓存的底层细节。

    在Java中包含了几个关键字:volatile、final和synchronized,帮助程序员把代码中的并发需求描述给编译器。Java内存模型中定义了它们的行为,确保正确同步的Java代码在所有的处理器架构上都能正确执行。

    synchronization 可以实现什么

    Synchronization有多种语义,其中最容易理解的是互斥,对于一个monitor对象,只能够被一个线程持有,意味着一旦有线程进入了同步代码块,那么其它线程就不能进入直到第一个进入的线程退出代码块(这因为都能理解)。

    但是更多的时候,使用synchronization并非单单互斥功能,Synchronization保证了线程在同步块之前或者期间写入动作,对于后续进入该代码块的线程是可见的(又是可见性,不过这里需要注意是对同一个monitor对象而言)。在一个线程退出同步块时,线程释放monitor对象,它的作用是把CPU缓存数据(本地缓存数据)刷新到主内存中,从而实现该线程的行为可以被其它线程看到。在其它线程进入到该代码块时,需要获得monitor对象,它在作用是使CPU缓存失效,从而使变量从主内存中重新加载,然后就可以看到之前线程对该变量的修改。

    但从缓存的角度看,似乎这个问题只会影响多处理器的机器,对于单核来说没什么问题,但是别忘了,它还有一个语义是禁止指令的重排序,对于编译器来说,同步块中的代码不会移动到获取和释放monitor外面。

    下面这种代码,千万不要写,会让人笑掉大牙:

    synchronized (new Object()) {
    }
    

    这实际上是没有操作的操作,编译器完成可以删除这个同步语义,因为编译知道没有其它线程会在同一个monitor对象上同步。

    所以,请注意:对于两个线程来说,在相同的monitor对象上同步是很重要的,以便正确的设置happens-before关系。

    final 可以影响什么

    如果一个类包含final字段,且在构造函数中初始化,那么正确的构造一个对象后,final字段被设置后对于其它线程是可见的。

    这里所说的正确构造对象,意思是在对象的构造过程中,不允许对该对象进行引用,不然的话,可能存在其它线程在对象还没构造完成时就对该对象进行访问,造成不必要的麻烦。

    class FinalFieldExample {
      final int x;
      int y;
      static FinalFieldExample f;
      public FinalFieldExample() {
        x = 3;
        y = 4;
      }
    
      static void writer() {
        f = new FinalFieldExample();
      }
    
      static void reader() {
        if (f != null) {
          int i = f.x;
          int j = f.y;
        }
      }
    }
    

    上面这个例子描述了应该如何使用final字段,一个线程A执行reader方法,如果f已经在线程B初始化好,那么可以确保线程A看到x值是3,因为它是final修饰的,而不能确保看到y的值是4。
    如果构造函数是下面这样的:

    public FinalFieldExample() { // bad!
      x = 3;
      y = 4;
      // bad construction - allowing this to escape
      global.obj = this;
    }
    

    这样通过global.obj拿到对象后,并不能保证x的值是3.

    volatile可以做什么

    Volatile字段主要用于线程之间进行通信,volatile字段的每次读行为都能看到其它线程最后一次对该字段的写行为,通过它就可以避免拿到缓存中陈旧数据。它们必须保证在被写入之后,会被刷新到主内存中,这样就可以立即对其它线程可以见。类似的,在读取volatile字段之前,缓存必须是无效的,以保证每次拿到的都是主内存的值,都是最新的值。volatile的内存语义和sychronize获取和释放monitor的实现目的是差不多的。

    对于重新排序,volatile也有额外的限制。

    下面看一个例子:

    class VolatileExample {
      int x = 0;
      volatile boolean v = false;
      public void writer() {
        x = 42;
        v = true;
      }
    
      public void reader() {
        if (v == true) {
          //uses x - guaranteed to see 42.
        }
      }
    }
    

    同样的,假设一个线程A执行writer,另一个线程B执行reader,writer中对变量v的写入把x的写入也刷新到主内存中。reader方法中会从主内存重新获取v的值,所以如果线程B看到v的值为true,就能保证拿到的x是42.(因为把x设置成42发生在把v设置成true之前,volatile禁止这两个写入行为的重排序)。

    如果变量v不是volatile,那么以上的描述就不成立了,因为执行顺序可能是v=true, x=42,或者对于线程B来说,根本看不到v被设置成了true。

    double-checked locking的问题

    臭名昭著的双重检查(其中一种单例模式),是一种延迟初始化的实现技巧,避免了同步的开销,因为在早期的JVM,同步操作性能很差,所以才出现了这样的小技巧。

    private static Something instance = null;
    
    public Something getInstance() {
      if (instance == null) {
        synchronized (this) {
          if (instance == null)
            instance = new Something();
        }
      }
      return instance;
    }
    

    这个技巧看起来很聪明,避免了同步的开销,但是有一个问题,它可能不起作用,为什么呢?因为实例的初始化和实例字段的写入可能被编译器重排序,这样就可能返回部门构造的对象,结果就是读到了一个未初始化完成的对象。

    当然,这种bug可以通过使用volatile修饰instance字段进行fix,但是我觉得这种代码格式实在太丑陋了,如果真要延迟初始化实例,不妨使用下面这种方式:

    private static class LazySomethingHolder {
      public static Something something = new Something();
    }
    
    public static Something getInstance() {
      return LazySomethingHolder.something;
    }
    

    由于是静态字段的初始化,可以确保对访问该类的所以线程都是可见的。

    对于这些,我们需要关心什么

    并发产生的bug非常难以调试,通常在测试代码中难以复现,当系统负载上来之后,一旦发生,又很难去捕捉,为了确保程序能够在任意环境正确的执行,最好是提前花点时间好好思考,虽然很难,但还是比调试一个线上bug来得容易的多。

    展开全文
  • 面试官:说说什么是 Java 内存模型(JMM)?

    千次阅读 多人点赞 2021-05-05 23:23:20
    本文禁止转载 1. 为什么要有内存模型...在面试中,面试官经常喜欢问:『说说什么是Java内存模型(JMM)?』 面试者内心狂喜,这题刚背过:『Java内存主要分为五大块:堆、方法区、虚拟机栈、本地方法栈、PC寄存器,ba.

    本文禁止转载

    在面试中,面试官经常喜欢问:『说说什么是Java内存模型(JMM)?』

    面试者内心狂喜,这题刚背过:『Java内存主要分为五大块:堆、方法区、虚拟机栈、本地方法栈、PC寄存器,balabala……』

    面试官会心一笑,露出一道光芒:『好了,今天的面试先到这里了,回去等通知吧』

    一般听到等通知这句话,这场面试大概率就是凉凉了。为什么呢?因为面试者弄错了概念,面试官是想考察JMM,但是面试者一听到Java内存这几个关键字就开始背诵八股文了。Java内存模型(JMM)和Java运行时内存区域区别可大了呢,不要走开接着往下看,答应我要看完。

    1. 为什么要有内存模型?

    要想回答这个问题,我们需要先弄懂传统计算机硬件内存架构。好了,我要开始画图了。

    1.1. 硬件内存架构

    在这里插入图片描述
    (1)CPU

    去过机房的同学都知道,一般在大型服务器上会配置多个CPU,每个CPU还会有多个,这就意味着多个CPU或者多个核可以同时(并发)工作。如果使用Java 起了一个多线程的任务,很有可能每个 CPU 都会跑一个线程,那么你的任务在某一刻就是真正并发执行了。

    (2)CPU Register

    CPU Register也就是 CPU 寄存器。CPU 寄存器是 CPU 内部集成的,在寄存器上执行操作的效率要比在主存上高出几个数量级。

    (3)CPU Cache Memory

    CPU Cache Memory也就是 CPU 高速缓存,相对于寄存器来说,通常也可以成为 L2 二级缓存。相对于硬盘读取速度来说内存读取的效率非常高,但是与 CPU 还是相差数量级,所以在 CPU 和主存间引入了多级缓存,目的是为了做一下缓冲。

    (4)Main Memory

    Main Memory 就是主存,主存比 L1、L2 缓存要大很多。

    注意:部分高端机器还有 L3 三级缓存。

    1.2. 缓存一致性问题

    由于主存与 CPU 处理器的运算能力之间有数量级的差距,所以在传统计算机内存架构中会引入高速缓存来作为主存和处理器之间的缓冲,CPU 将常用的数据放在高速缓存中,运算结束后 CPU 再讲运算结果同步到主存中。

    使用高速缓存解决了 CPU 和主存速率不匹配的问题,但同时又引入另外一个新问题:缓存一致性问题。
    在这里插入图片描述
    在多CPU的系统中(或者单CPU多核的系统),每个CPU内核都有自己的高速缓存,它们共享同一主内存(Main Memory)。当多个CPU的运算任务都涉及同一块主内存区域时,CPU 会将数据读取到缓存中进行运算,这可能会导致各自的缓存数据不一致。

    因此需要每个 CPU 访问缓存时遵循一定的协议,在读写数据时根据协议进行操作,共同来维护缓存的一致性。这类协议有 MSI、MESI、MOSI、和 Dragon Protocol 等。

    1.3. 处理器优化和指令重排序

    为了提升性能在 CPU 和主内存之间增加了高速缓存,但在多线程并发场景可能会遇到缓存一致性问题。那还有没有办法进一步提升 CPU 的执行效率呢?答案是:处理器优化。

    为了使处理器内部的运算单元能够最大化被充分利用,处理器会对输入代码进行乱序执行处理,这就是处理器优化。

    除了处理器会对代码进行优化处理,很多现代编程语言的编译器也会做类似的优化,比如像 Java 的即时编译器(JIT)会做指令重排序。
    在这里插入图片描述

    处理器优化其实也是重排序的一种类型,这里总结一下,重排序可以分为三种类型:

    • 编译器优化的重排序。编译器在不改变单线程程序语义放入前提下,可以重新安排语句的执行顺序。
    • 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
    • 内存系统的重排序。由于处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

    2. 并发编程的问题

    上面讲了一堆硬件相关的东西,有些同学可能会有点懵,绕了这么大圈,这些东西跟 Java 内存模型有啥关系吗?不要急咱们慢慢往下看。

    熟悉 Java 并发的同学肯定对这三个问题很熟悉:『可见性问题』、『原子性问题』、『有序性问题』。如果从更深层次看这三个问题,其实就是上面讲的『缓存一致性』、『处理器优化』、『指令重排序』造成的。

    在这里插入图片描述
    缓存一致性问题其实就是可见性问题,处理器优化可能会造成原子性问题,指令重排序会造成有序性问题,你看是不是都联系上了。

    出了问题总是要解决的,那有什么办法呢?首先想到简单粗暴的办法,干掉缓存让 CPU 直接与主内存交互就解决了可见性问题,禁止处理器优化和指令重排序就解决了原子性和有序性问题,但这样一夜回到解放前了,显然不可取。

    所以技术前辈们想到了在物理机器上定义出一套内存模型, 规范内存的读写操作。内存模型解决并发问题主要采用两种方式:限制处理器优化使用内存屏障

    3. Java 内存模型

    同一套内存模型规范,不同语言在实现上可能会有些差别。接下来着重讲一下 Java 内存模型实现原理。

    3.1. Java 运行时内存区域与硬件内存的关系

    了解过 JVM 的同学都知道,JVM 运行时内存区域是分片的,分为栈、堆等,其实这些都是 JVM 定义的逻辑概念。在传统的硬件内存架构中是没有栈和堆这种概念。
    在这里插入图片描述
    从图中可以看出栈和堆既存在于高速缓存中又存在于主内存中,所以两者并没有很直接的关系。

    3.2. Java 线程与主内存的关系

    Java 内存模型是一种规范,定义了很多东西:

    • 所有的变量都存储在主内存(Main Memory)中。
    • 每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的拷贝副本。
    • 线程对变量的所有操作都必须在本地内存中进行,而不能直接读写主内存。
    • 不同的线程之间无法直接访问对方本地内存中的变量。

    看文字太枯燥了,我又画了一张图:
    在这里插入图片描述

    3.3. 线程间通信

    如果两个线程都对一个共享变量进行操作,共享变量初始值为 1,每个线程都变量进行加 1,预期共享变量的值为 3。在 JMM 规范下会有一系列的操作。
    在这里插入图片描述
    为了更好的控制主内存和本地内存的交互,Java 内存模型定义了八种操作来实现:

    • lock:锁定。作用于主内存的变量,把一个变量标识为一条线程独占状态。
    • unlock:解锁。作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
    • read:读取。作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
    • load:载入。作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
    • use:使用。作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
    • assign:赋值。作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
    • store:存储。作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
    • write:写入。作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

    注意:工作内存也就是本地内存的意思。

    4. 有态度的总结

    由于CPU 和主内存间存在数量级的速率差,想到了引入了多级高速缓存的传统硬件内存架构来解决,多级高速缓存作为 CPU 和主内间的缓冲提升了整体性能。解决了速率差的问题,却又带来了缓存一致性问题。

    数据同时存在于高速缓存和主内存中,如果不加以规范势必造成灾难,因此在传统机器上又抽象出了内存模型。

    Java 语言在遵循内存模型的基础上推出了 JMM 规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。

    为了更精准控制工作内存和主内存间的交互,JMM 还定义了八种操作:lock, unlock, read, load,use,assign, store, write

    – End –

    关于Java 内存模型还有很多东西没有展开讲,比如说:内存屏障happens-before锁机制CAS等等。要肝一个系列了,加油!

    作者:雷小帅

    推荐一个Github 开源项目,『Java八股文』Java面试套路,Java进阶学习,打破内卷拿大厂Offer,升职加薪!https://github.com/CoderLeixiaoshuai/java-eight-part

    作者简介: ☕读过几年书:华中科技大学硕士毕业;
    😂浪过几个大厂:华为、网易、百度……
    😘一直坚信技术能改变世界,愿保持初心,加油技术人!

    微信搜索公众号【爱笑的架构师】,关注这个对技术有追求且有趣的打工人。

    展开全文
  • 讲一讲什么是Java内存模型 Java内存模型虽说是一个老生常谈的问题 ,也是大厂面试中绕不过的,甚至初级面试也会问到。但是真正要理解起来,还是相当困难,主要这个东西看不见,摸不着。 这是一个比较开放的题目,...
  • 在了解什么是 Java 内存模型之前,先了解一下为什么要提出Java 内存模型。 之前提到过并发编程有三大问题 CPU 缓存,在多核 CPU 的情况下,带来了可见性问题 操作系统对当前执行线程的切换...那么什么是Java 内存...

    在了解什么是 Java 内存模型之前,先了解一下为什么要提出 Java 内存模型。

    之前提到过并发编程有三大问题

    • CPU 缓存,在多核 CPU 的情况下,带来了可见性问题
    • 操作系统对当前执行线程的切换,带来了原子性问题
    • 译器指令重排优化,带来了有序性问题

    为了解决并发编程的三大问题,提出了 JSR-133,新的 Java 内存模型,JDK 5 开始使用。

     

    那么什么是 Java 内存模型呢?

    现在说的 Java 内存模型,一般是指 JSR-133: Java Memory Model and Thread Specification Revision 规定的 Java 内存模型。

    JSR-133 具体描述:jsr133.pdf

    JSR-133 在 JCP 官网的具体描述

     

    说明下

    JSR:Java Specification Requests,Java 规范提案。

    JCP:Java Community Process 是一个开放的国际组织,成立于1998年,主要由 Java 开发者以及被授权者组成,是使有兴趣的各方参与定义 Java 的特征和未来版本的正式过程。

     

    简单总结下

    • Java 内存模型是 JVM 的一种规范
    • 定义了共享内存在多线程程序中读写操作行为的规范
    • 屏蔽了各种硬件和操作系统的访问差异,保证了 Java 程序在各种平台下对内存的访问效果一致
    • 解决并发问题采用的方式:限制处理器优化和使用内存屏障
    • 增强了三个同步原语(synchronized、volatile、final)的内存语义
    • 定义了 happens-before 规则

     

    参考:
    1、https://baike.baidu.com/item/JSR
    2、https://baike.baidu.com/item/JCP
    3、https://zh.wikipedia.org/zh-hans/JCP

     

     


    【Java面试题与答案】整理推荐

     

    展开全文
  • 什么是Java内存模型

    2019-03-11 22:54:47
    文章目录前言CPU工作过程及出现的问题CPU执行过程缓存一致性问题处理器重排序问题什么是计算机内存模型什么是Java内存模型总结参考 前言 要想深入了解Java并发编程,就要先理解好Java内存模型,而要理解Java内存...
  • 别再问什么是Java内存模型了,看这里! 网上有很多关于Java内存模型的文章,在《深入理解Java虚拟机》和《Java并发编程的艺术》等书中也都有关于这个知识点的介绍。但是,很多人读完之后还是搞不清楚,甚至有的人说...
  • 别再问什么是Java内存模型了,看这里! 网上有很多关于Java内存模型的文章,在《深入理解Java虚拟机》和《Java并发编程的艺术》等书中也都有关于这个知识点的介绍。但是,很多人读完之后还是搞不清楚,甚至有的人说...
  • Java内存模型是一个老生常谈的问题 ,大厂面试中绕不过的,不仅中级和高级程序员面试甚至初级面试也会问到。但是真正要理解起来,还是相当困难,主要这个东西看不见,摸不着。网上已经有大量的博客,但是人家的...
  • 在面试中,面试官经常喜欢问:『说说什么是Java内存模型(JMM)?』面试者内心狂喜,这题刚背过:『Java内存主要分为五大块:堆、方法区、虚拟机栈、本地方法栈、PC寄存器,balabal...
  • 面试官:什么是Java内存模型?前言不要为了读文章而读文章,一定要带着问题来读文章,勤思考。要想深入了解Java并发编程,就要先理解好Java内存模型,而要理解Java内存模型又不得不从硬件、计算机内存模型说起,本文...
  • 什么是java内存模型 缓存一致性问题 在现代计算机中,因为CPU的运算速度远大于内存的读写速度,因此为了不让CPU在计算的时候因为实时读取内存数据而影响运算速度,CPU会加入一层缓存,在运算之前缓存内存的数据,...
  • 了解 Java 并发的底层原理,那么 Java 内存模型的知识非常重要,同时也一个分水岭,可以区分出我们仅停留在如何使用并发工具,还是能更进一步,知其所以然。 容易混淆:JVM 内存结构 VS Java 内存模型 Java 作为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,169
精华内容 1,667
关键字:

什么是java内存模型

java 订阅