Java 内存模型(JMM)

23

注意:Java 内存模型(JMM)和 Java 运行时内存区域是两个概念!

【前置】共享变量是什么:

什么是共享变量?

对于每一个线程来说,栈都是私有的,而堆是共有的。

也就是说,在栈中的变量(局部变量、方法定义的参数、异常处理的参数)不会在线程之间共享,也就不会有内存可见性的问题,也不受内存模型的影响。而在堆中的变量是共享的,一般称之为共享变量。

所以,内存可见性针对的是堆中的共享变量

Java内存模型是什么

JMM(Java Memory Model)Java内存模型,是java虚拟机规范中所定义的一种内存模型。

Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。

特点:

  1. 所有的共享变量都存储于主内存(计算机的RAM)这里所说的变量指的是实例变量和类变量。不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。

  2. 每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本。

  3. 线程对变量的所有的操作(读,写)都必须在工作内存中完成,而不能直接读写主内存中的变量,不同线程之间也不能直接访问对方工作内存中的变量,线程间变量的值的传递需要通过主内存完成

疑问:那其他线程怎么知道的主内存数据被改了?

如何保证内存可见性?

JMM 通过控制主存与每个线程的本地内存之间的交互,来提供内存可见性保证

Java 中的 volatile 关键字可以保证多线程操作共享变量的可见性以及禁止指令重排序,synchronized 关键字不仅保证可见性,同时也保证了原子性(互斥性)。

在更底层,JMM 通过内存屏障来实现内存的可见性以及禁止重排序。为了程序员更方便地理解,设计者提出了 happens-before 的概念,它更加简单易懂,从而避免了程序员为了理解内存可见性而去学习复杂的重排序规则,以及这些规则的具体实现方法。

共享变量的可见性

即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。

禁止进行指令重排序

  • 写操作加的屏障是阻止上方其它写操作越过屏障排到volatile变量写之下

  • 读操作加的屏障是阻止下方其它读操作越过屏障排到volatile变量读之上

三角形尖对着哪儿哪儿就被挡住

总结一个volatile使用的小妙招:

  • 写变量让volatile修饰的变量的在代码最后位置

  • 读变量让volatile修饰的变量的在代码最开始位置

JMM 与 Java 运行时内存区域的区别

前面提到了 JMM 和 Java 运行时内存区域的划分,这两者既有差别又有联系:

  • 区别

    两者是不同的概念。JMM 是抽象的,他是用来描述一组规则,通过这个规则来控制各个变量的访问方式,围绕原子性、有序性、可见性等展开。而 Java 运行时内存的划分是具体的,是 JVM 运行 Java 程序时必要的内存划分。

  • 联系

    都存在私有数据区域和共享数据区域。一般来说,JMM 中的主存属于共享数据区域,包含了堆和方法区;同样,JMM 中的本地内存属于私有数据区域,包含了程序计数器、本地方法栈、虚拟机栈。