精进之路之JMM

JMM (Java Memory Model) java内存模型java

Java内存模型的抽象

Java线程之间的通讯由Java内存模型(本文简称为JMM)控制,JMM决定一个线程对共享变量的写入什么时候对另外一个线程可见。缓存

Java内存模型规定了全部的变量都存储在主内存(Main Memory)中。每条线程还有本身的工做内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工做内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的全部操做(读取、赋值等)都必须在工做内存中进行,而不能直接读写主内存中的变量。不一样的线程之间也没法直接访问对方工做内存中的变量,线程间变量值的传递均须要经过主内存来完成,线程、主内存、工做内存三者的交互关系如图所示。多线程

本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其余的硬件和编译器优化app

 

1. 重排序

为了程序可以更高效的运行,编译器和处理器都会对指令进行重排序;重排序分为如下三种类型:优化

  1. 编译器优化的重排序
  2. 指令级并行的重排序
  3. 内存系统的重排序

只要是重排序都有可能会致使多线程内出现内存可见性的问题spa

1.1 内存屏障指令

为了保证内存可见性,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会使该屏障以前的全部内存访问指令(存储和装载指令)完成以后,才执行该屏障以后的内存访问指令。

1.2 happens-before

若是一个操做执行的结果须要对另外一个操做可见,那么这两个操做之间必须存在happens-before关系。这里提到的两个操做既能够是在一个线程以内,也能够是在不一样线程之间。blog

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仅仅要求前一个操做(执行的结果)对后一个操做可见,且前一个操做按顺序排在第二个操做以前内存

2. 顺序一致性模型

JMM对正确同步的多线程程序,其执行将具备顺序一致性(sequentially consistent)---即程序的执行结果和在顺序一致性模型中获得的结果相同;(这里同步是指广义上的同步,包括同步原语(lock,volatile,final)的正确使用)

特性:

  • 一个线程中全部操做都必须按照程序的顺序来执行
  • 无论程序是否同步,全部线程都只能看到一个单一的操做执行顺序。在此模型下,每一个操做都必须原子执行且当即对全部线程可见
本文转自: https://www.jianshu.com/p/75f512f22c23,感谢原做者的精彩分享
相关文章
相关标签/搜索