
jmm通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证
很多处理器内存模型允许读/写重排序,所以jmm展示一个一致的内存模型,在重排序的地方插入内存屏障,禁止处理器重排序。所以称JMM是语言级的内存模型

只要多线程程序是正确同步的,jmm保证该程序在任意处理器平台上的执行结果,与该程序在顺序一致性内存模型中的执行结果一致
对会改变程序执行结果的重排序,jmm会要求编译器和处理器禁止这种重排序。
对于不会改变程序执行结果的重排序,jmm对编译器和处理器不做要求
屏蔽了各种硬件和操作系统对内存访问的差异性(多线程情况下会有编译器优化和指令优化),从而实现让java程序在各种平台下都能达到一致的并发效果。
三种排序

1属于编译器重排序,2、3属于处理器重排序,这些重排序会导致多线程程序出现内存可见性问题
jmm编译器重排序规则禁止特定类型的编译器重排序,处理器重排序规则会要求java编译器生成指令的时候,插入内存屏障指令来禁止处理器重排序。

如果想要一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,否则重排序会导致一个操作对另一个操作不可见。
程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作
监视器锁规则:解锁happens-before对同一个锁的加锁
volatile变量规则:对一个volatile域写,happens-before后续对这个volatile域的读
传递性:a happens-before b,b happens-before c,则a happens-before c
happens-before仅仅要求前一个操作对后一个操作可见,且前一个操作按顺序排在第二个操作前面,但不一定前一个操作先于后一个操作执行,因为某些情况允许重排
补充:
程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。
对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。


实现方式:
volatile写之前的操作不会被重排到volatile写之后
volatile读之后的操作不会被重排到volatile读之前
第一个操作是volatile写,第二个操作是volatile读时,不能重排序
volatile写前面会插入一个StoreStore屏障,后面会插入StoreLoad屏障
volatile读后面会插入loadload屏障和loadstore屏障
普通读写可以重排在后面,volatile读写不能重排在后面
|
|
volatile读
|
|
普通读写不能重排到上面,volatile读写不能重排在上面
普通读写不能重排到后面,volatile读写不能重排在后面
|
|
volatile写
|
|
普通读写可以重排到上面,volatile读写不能重排在上面


利用了volatile来保证解锁happens-before对同一个锁的加锁,锁释放与volatile写有相同语义,锁获取与volatile读有相同的内存语义
在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,则这两个操作之间不能重排序
初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序
https://www.infoq.cn/article/java_memory_model深入理解java内存模型