java内存模型(JSR-133内存模型)

定义:

Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM),用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,JMM规范了Java虚拟机与计算机内存是如何协同工作的:规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。

JMM主要包扩线程、工作内存、主内存三者来交互。Java内存模型的三大特性包括:原子性、可见性、有序性

JMM模型下的线程间通信:

java内存模型(JSR-133内存模型)

 

1)线程A把本地内存A中更新过的共享变量刷新到主内存中去。

2)线程B到主内存中去读取线程A之前已更新过的共享变量。

Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的所有操作(读、写)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同线程之间无法直接访问对方工作内存中的变量,线程间通信均需要在主内存来完成

 

重排序:

重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。分为:编译器优化的重排序指令级并行的重排序内存系统的重排序

编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序(如:a=1 b=a不会重排序)。(这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑)

重排序可能会导致多线程程序出现内存可见性问题。对于处理器重排序,JMM的处理器重排序规则会要求Java编译器在生成指令序列时,插入特定类型的内存屏障(Memory Barriers,Intel称之为Memory Fence)指令,通过内存屏障指令来禁止特定类型的处理器重排序。通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

happens-before:JSR-133使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行结果需要对另一个操作可见,那么扎两个操作之间必须存在happens-before的关系。(这里的两个操作是在一个线程之间也可以在不同线程之间)happens-before 仅仅要求前一个操作对都一个操作可见,且前一个操作的顺序排在第二个之前

happens-before规则如下:

1)线程顺序规则:一个线程中的每个操作,happens-before与该线程中的任意后续操作

2)监视器锁规则:对一个锁的解锁,happens-before与随后对这个锁的加锁

3)volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读

4)传递性:如果A happens-before B,且B happens-before C,那么A happens-before C

as-if-serial语义:

不管怎么重排序,单线程程序的结果不能被改变。编译器、处理器都必须遵守as-if-serial语义

在举一个例子:

java内存模型(JSR-133内存模型)

上面三个操作的数据依赖性如下图所示

java内存模型(JSR-133内存模型)

可以看到,A和B并不存在数据依赖性,所程序的执行顺序就有两种可能,A-B-C或B-A-C,并且执行结果都为3.14

在这里,as-if-serial语义吧单线程程序保护了起来,使得程序员无需担心重排序会干扰他们。

根据happens-before的程序顺序规则,在上面的例子中存在3个happens-before关系:

1)A  happens-before B

2)B happens-before C

3)A happens-before C

这里A happens-before B,但实际执行时B却可以排在A前面(上面第二种情况),如果A happens-before B,Java内存模型并不要求A一定在B之前执行。Java内存模型仅仅要求前一个操作的执行结果对后一个操作可见,且前一个操作按顺序排在第二个操作之前。在这里操作A的执行结果并不需要对操作B可见,所以在这种情况下,Java内存模型认为这种重排序并不非法。

 

上一篇:JSR - 133 都解决了哪些问题?


下一篇:救济金发放( The Dole Queue, UVa 133)