当前位置: 首页 > 后端技术 > Java

JMM(JavaMemoryModel)

时间:2023-04-01 21:29:47 Java

JMMJavaMemoryModel,即java内存模型,定义在JSR-133规范中。JSR-133是在1997年,此时在Java版本中的内存模型中发现了几个严重的缺陷。这个缺陷经常会导致奇怪的问题,比如字段值经常变化,很容易削弱编译器的优化能力。为了修复这些缺陷,JSR-133专家组提出了JSR-133规范。该规范是JSR-176的一部分,它定义了JavaTMPlatformTiger(5.0)版本的主要特性。本规范的标准内容将合并到JavaTM语言规范、JavaTM虚拟机规范和java.lang包的类描述中。JSR-133的设计目标主要包括1.保留Java已有的安全保证,比如类型安全,加强其他的安全保证,比如一个线程观察到的每个变量的值必须被一个线程修改后。2.程序的同步语义应该尽可能简单直观。3.将多线程如何交互的细节留给程序员。4.在广泛流行的硬件架构上设计正确的、高性能的JVM实现。5.应提供初始化安全的保证。如果一个对象被正确构造,所有看到对象构造的线程都可以看到构造函数中设置的final字段的值,而无需任何同步操作。6.对现有代码的影响要尽可能小。JMM结构规范JMM规定所有变量都存储在主存(MainMemory)中。每个线程也有自己的工作内存(WorkingMemory)。线程的工作内存存储线程使用的变量的主内存副本。所有操作(读、赋值等)都是在工作内存中进行的,但不能直接读写主内存中的变量(volatile变量仍然有工作内存的一份副本,但由于其特殊的操作顺序要求,看起来像是直接在主存中读写)。不同的线程不能直接访问彼此工作内存中的变量,线程之间的值传递需要通过主内存来完成。个人理解:JVM中的虚拟机栈(线程私有)和堆内存(线程共享)可以看作是上述规范的一种实现。原子JMM保证的原子变量操作包括read、load、assign、use,store、write、long和double非原子契约引起的非原子操作基本可以忽略。如果需要对更大范围的代码实现原子操作,就需要JMM提供的lock、unlock、synchronized等来保证。可见性在前面分析volatile语义时提到过,可见性是指当一个线程修改共享变量的值时,其他线程可以立即知道修改。JMM在变量修改后将新值同步回主存,以主存为媒介,在线程读取变量前从内存中刷新变量的新值,保证变量的可见性多变的。对于普通变量和volatile变量都是如此,但是volatile的特殊规则保证了这种可见性是立即知道的,而普通变量则没有这种严格的可见性。除了volatile,synchronized和final也能保证可见性。有序性JMM的有序性表明,如果在这个线程中观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,则所有操作都是无序的。前半句指的是“线程内的仿串行语义”(as-if-serial),后半句指的是“指令重排序”现象和worker之间的同步延迟内存和普通变量的主内存。重新排序为了提高程序执行期间的性能,编译器和处理器经常对指令进行重新排序。从硬件架构的角度来看,指令重排序是指CPU不按照程序指定的顺序,而是采用让多条指令分别发送给对应的各个电路单元进行处理的方式,而不是任意重新排列指令。禁止重排序的意义重排序对单线程没有影响,但是在多线程的情况下,A线程初始化一个对象,可能被B线程使用。当允许重排序时,B线程可能会获取到一个未初始化的对象,即线程A先分配对象指针,然后进行初始化,此时会对线程B产生影响。JMM重新排序屏障LoadLoadBarriers:确保Load1数据在Load2和所有后续加载指令之前加载LoadStoreBarriers:确保Load1数据在Store2之前加载,并且所有后续存储指令都被刷新到内存StoreLoadBarriers:确保Store1数据在加载之前对其他处理器可见(刷新到内存,其他处理器的缓存行无效)Load2和所有后续加载指令。该指令将导致屏障之前的所有内存访问指令完成,然后才能执行屏障之后的内存访问指令。happens-before原则happens-before上面提到的内存交互操作必须满足一定的规则,happens-before是定义这些规则的等价判断原则。happens-before是JMM定义的两个操作之间的偏序关系:如果操作A线性发生在操作B中,操作B可以观察到A的影响,“影响”包括修改内存中共享变量的值,发送一个消息,调用一个方法等。如果两个操作满足happens-before原则,那么就不需要进行同步操作,JVM可以保证操作是顺序的,此时不能随意重新排序.否则无法保证顺序,可以进行指令的重新排序。happens-before1.程序顺序规则(ProgramOrderRule):在同一个线程中,根据程序代码的先后顺序,写在前面的操作先于写在后面的操作发生。准确的说是程序的控制流顺序,考虑分支和循环等。2.易失性变量规则(VolatileVariableRule):对易失性变量的写操作先发生在对该变量的读操作之后(时间顺序)。3、线程启动规则(ThreadStartRule):Thread对象的start()方法先于这个线程的每一个动作。4.线程终止规则(ThreadTerminationRule):线程的所有操作都发生在线程终止检测之前。通过Thread.join()方法的结束、Thread.isAlive()的返回值等可以检测到线程已经Terminate执行。5、线程中断规则(ThreadInterruptionRule):当被中断线程的代码检测到中断发生时,首先调用线程中断()方法。Thread.interrupted()可以检测是否有中断发生。6.对象终结规则(FinilizerRule):对象的初始化完成(构造函数执行结束)在它的finalize()开始之前。7.传递性:如果操作A发生在操作B之前,操作B发生在操作C之前,那么可以断定A发生在操作C之前。注意:不同操作的时间顺序与先发生的原则没有关系。两者不能相互推断。并发安全问题的度量不能被时间顺序打乱。一切都要基于happens-before原则参考:https://blog.csdn.net/u011080...https://blog.csdn.net/zjcjava...https://blog.csdn.net/zjcjava...https://www.cnblogs.com/cxuan...