JMM(Java Memory Model)
前言
导致可见性是因为CPU缓存,导致顺序性是因为编译优化,意味着解决可见性和顺序性的办法是:禁用CPU缓存和编译优化,但是这样会导致程序性能下降严重。
为此不得不做出一个合理的取舍,相对合理的办法就是按需禁用CPU缓存和按需优化。
这里就涉及到对于java程序员不得不知的JMM,Java内存模型是个很复杂的规范,需要我们从多个维度来看待:
1、内存模型概念可以理解为在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。不同架构的物理计算机可以有不一样的内存模型,JVM也有自己的内存模型。
2、JVM中试图定义一种Java内存模型(Java Memory Model, JMM)来屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。
3、从开发者角度而言Java内存模型描述了在多线程代码中哪些行为是合法的,以及线程如何通过内存进行交互。它描述了“程序中的变量” 和 “从内存或者寄存器获取或存储它们的底层细节”之间的关系。
Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法。
具体来说,这些方法包括 volatile、synchronized 和 final 三个关键字,以及Happens-Before规则
主内存与工作内存
JMM的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。
此处的变量(Variables)与Java编程中的变量有所区别,它包括了实例字段、静态字段和构成数值对象的元素,不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然就不会存在竞争问题。
JMM规定了所有的变量都存储在主内存(Main Memory)中。
每条线程还有自己的工作内存(Working Memory),工作内存中保留了该线程使用到的变量的主内存的副本。工作内存是 JMM 的一个抽象概念,并不真实存在,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

线程对变量的所有操作都必须在工作内存中进行,不能直接读写主内存中的变量。
不同的线程间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
注意:为了获得较好的执行效能,
1、JMM并没有限制执行引擎使用处理器的特定寄存器或缓存来和主存进行交互
2、JMM也没有限制即时编译器调整指令执行顺序这类优化措施
JMM解决的问题
1、工作内存数据一致性:可见性问题
各个线程操作数据时会使用工作内存中的主内存中共享变量副本,当多个线程的运算任务都涉及同一个共享变量时,可能导致各自的共享变量副本不一致。如果真的发生这种情况,数据同步回主内存以谁的副本数据为准?
Java 内存模型主要通过一系列的数据同步协议、规则来保证数据的一致性。
2、约束指令重排序优化:有序性问题
Java 中重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序可分为两类:编译期重排序和运行期重排序(处理器乱序优化),分别对应编译时和运行时环境。
同样的,指令重排序不是随意重排序,它需要满足以下几个条件:
在单线程环境下不能改变程序运行的结果。即时编译器(和处理器)需要保证程序能够遵守 as-if-serial 属性。通俗地说,就是在单线程情况下,要给程序一个顺序执行的假象。即使经过重排序后的执行结果要与顺序执行的结果保持一致。
存在数据依赖关系的不允许重排序。
多线程环境下,如果线程处理逻辑之间存在依赖关系,有可能因为指令重排序导致运行结果与预期不同。
                    
                
                
            
        
浙公网安备 33010602011771号