Java - 内存模型(JMM)
2020-04-07 本文已影响0人
万福来
java内存模型是什么
java内存模型和jvm内存结构不是一回事,JMM是为了解决java并发问题提供的一种解决方案。
它定义了线程和主内存之间的抽象关系。线程之间的共享变量存储在主内存中,每个线程都有自己的工作内存,工作内存中存储了该线程读写共享变量的副本。
happens-before关系
在JMM中,如果一个操作的执行结果需要对另一个操作可见,那么这两个操作之间必须要存在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:不管怎么重排序,单线程下的执行结果不能被改变,编译器、runtime和处理器都必须遵守as-if-serial语义。
volatile 和 final
- volatile:volatile就是JMM为了解决内存可见性问题提供的关键字修饰词,当一个共享变量被volatile修饰时,它会保证修改的值立即被更新到主内存,其他线程也会直接从主内存读取该变量最新的值。该关键字就是通过内存屏障(特殊的CPU指令)来禁止指令重排序。
- final:通过finla修饰的变量也可以禁止部分指令重排序,比如在一个构造函数内对一个final修饰的变量赋值和把构造对象引用复制给其他引用,这两个操作不能重排序,还有就是初次读final修饰的变量与随后读这个变量不能重排序。