JMM (Java Memory Model) java内存模型java
Java线程之间的通讯由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入什么时候对另外一个线程可见。缓存
Java内存模型规定了全部的变量都存储在主内存(Main Memory)中。每条线程还有本身的工做内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工做内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的全部操做(读取、赋值等)都必须在工做内存中进行,而不能直接读写主内存中的变量。不一样的线程之间也没法直接访问对方工做内存中的变量,线程间变量值的传递均须要经过主内存来完成,线程、主内存、工做内存三者的交互关系如图所示。多线程
本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其余的硬件和编译器优化app
为了程序可以更高效的运行,编译器和处理器都会对指令进行重排序;重排序分为如下三种类型:优化
只要是重排序都有可能会致使多线程内出现内存可见性的问题spa
为了保证内存可见性,java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。JMM把内存屏障指令分为下列四类:线程
屏障类型 | 指令示例 | 说明 |
---|---|---|
LoadLoad Barriers | Load1; LoadLoad; Load2 | 确保Load1数据的装载,以前于Load2及全部后续装载指令的装载。 |
StoreStore Barriers | Store1; StoreStore; Store2 | 确保Store1数据对其余处理器可见(刷新到内存),以前于Store2及全部后续存储指令的存储。 |
LoadStore Barriers | Load1; LoadStore; Store2 | 确保Load1数据装载,以前于Store2及全部后续的存储指令刷新到内存。 |
StoreLoad Barriers | Store1; StoreLoad; Load2 | 确保Store1数据对其余处理器变得可见(指刷新到内存),以前于Load2及全部后续装载指令的装载。StoreLoad Barriers会使该屏障以前的全部内存访问指令(存储和装载指令)完成以后,才执行该屏障以后的内存访问指令。 |
若是一个操做执行的结果须要对另外一个操做可见,那么这两个操做之间必须存在happens-before关系。这里提到的两个操做既能够是在一个线程以内,也能够是在不一样线程之间。blog
happens-before规则以下:排序
注意,两个操做之间具备happens-before关系,并不意味着前一个操做必需要在后一个操做以前执行!happens-before仅仅要求前一个操做(执行的结果)对后一个操做可见,且前一个操做按顺序排在第二个操做以前内存
JMM对正确同步的多线程程序,其执行将具备顺序一致性(sequentially consistent)---即程序的执行结果和在顺序一致性模型中获得的结果相同;(这里同步是指广义上的同步,包括同步原语(lock,volatile,final)的正确使用)
特性: