对Java内存模型即JMM的理解

简介: 类似物理上的计算机系统,Java虚拟机规范中也定义了一种Java内存模型,即Java Memory Model(JMM), 来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果。

类似物理上的计算机系统,Java虚拟机规范中也定义了一种Java内存模型,即Java Memory Model(JMM),
来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果。
现在最新的Java内存模型规范是JSR-133,即Java内存模型与线程规范,这套规范包含:
线程之间如何通过内存通信;
线程之间通过什么方式通信才合法,才能得到期望的结果。

理解内存模型对Java的并发编程有很大的帮助。

Java内存模型

JMM决定一个线程对共享变量的写入何时对另一个线程可见。
定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。
本地内存是一个抽象概念,并不真实存在,涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

(1)主内存和本地内存
主内存即main memory。在java中,实例域、静态域和数组元素是线程之间共享的数据,它们存储在主内存中。
本地内存即local memory。 局部变量,方法定义参数 和 异常处理器参数是不会在线程之间共享的,它们存储在线程的本地内存中。
(2)原子性
是指一个操作是按原子的方式执行的。要么该操作不被执行;要么以原子方式执行,即执行过程中不会被其它线程中断。

重排序

重排序是指“编译器和处理器”为了提高性能,而在程序执行时会对程序进行的重排序。
重排序分为——“编译器”和“处理器”两个方面,而“处理器”重排序又包括“指令级重排序”和“内存的重排序”。
关于重排序,我们需要理解它的思想:为了提高程序的并发度,从而提高性能!但是对于多线程程序,重排序可能会导致程序执行的结果不是我们需要的结果!因此,就需要我们通过“volatile,synchronize,锁等方式”作出正确的实现同步。

 

内存屏障

内存屏障包括LoadLoad, LoadStore, StoreLoad, StoreStore共4种内存屏障。内存屏障是与相应的内存重排序相对应的。
通过内存屏障可以禁止特定类型处理器的重排序,从而让程序按我们预想的流程去执行。

 

顺序一致性

顺序一致性内存模型是理想化的内存模型。有以下规则:
(1)一个线程中的所有操作必须按照程序的顺序来执行。
(2)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。

 

锁是java并发编程中最重要的同步机制。
(1)锁的内存语义:
线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。

 

(2)JMM如何实现锁
公平锁是通过“volatile”实现同步的。公平锁在释放锁的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见。

非公平锁通过CAS实现的,CAS就是compare and swap。CAS实际上调用的JNI函数,也就是CAS依赖于本地实现。以Intel来说,对于CAS的JNI实现函数,它保证:
禁止该CAS之前和之后的读和写指令重排序。
把写缓冲区中的所有数据刷新到内存中。

 

final域

对于基本类型的final域,编译器和处理器要遵守两个重排序规则:
(1)final写:“构造函数内对一个final域的写入”,与“随后把这个被构造对象的引用赋值给一个引用变量”,这两个操作之间不能重排序。
(2)final读:“初次读一个包含final域的对象的引用”,与“随后初次读对象的final域”,这两个操作之间不能重排序。
对于引用类型的final域,除上面两条之外,还有一条规则:
(3)final写:在“构造函数内对一个final引用的对象的成员域的写入”,与“随后在构造函数外把这个被构造对象的引用赋值给一个引用变量”,这两个操作之间不能重排序。
写final域的重排序规则可以确保在引用变量为任意线程可见之前,该引用变量指向的对象的final域已经在构造函数中被正确初始化过了。其实要得到这个效果,还需要一个保证:在构造函数内部,不能让这个被构造对象的引用为其他线程可见,也就是对象引用不能在构造函数中“逸出”。
JMM通过“内存屏障”实现final,在final域的写之后,构造函数return之前,插入一个StoreStore障屏。在读final域的操作前面插入一个LoadLoad屏障。


目录
相关文章
|
9月前
|
存储 缓存 安全
Java内存模型JMM
计算机存储结构,从本地磁盘到主内存到CPU缓存,也就是从硬盘到内存,到CPU,一般对应的程序的操作就是从数据库查数据到内存然后到CPU进行计算 因为有这么多级的缓存(CPU和物理主内存的速度是不一样的) CPU的运行并不是直接操作内存而是先把内存里边的数据读到缓存,而内存的读和写操作的时候就会造成不一致的问题 JVM规范中试图定义一种java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异 以实现让java程序在各种平台下都能达到一致的内存访问效果
48 0
|
10月前
|
缓存 Java 编译器
05.深入理解JMM和Happens-Before
大家好,我是王有志。今天我们一起来学习Java并发编程中最重要的两个理论知识JMM和Happens-Before原则。
68 1
05.深入理解JMM和Happens-Before
|
11月前
|
存储 缓存 Java
关于JMM的理解
JMM是java内存模型,它描述的是和多线程相关的一组规范。通过这组规范定义了程序中对各个变量的访问方式。保证了不同jvm运行并发程序的结果的一致性和可靠性。
63 0
|
存储 缓存 Java
java内存模型JMM
Java内存模型(Java Memory Model,简称JMM),即Java虚拟机定义的一种用来屏蔽各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能够达到一致的内存访问效果的内存模型。本篇文章大致涉及到五个要点:1、Java内存模型的基础,主要介绍JMM抽象结构;2、Java内存模型中内存屏障;3、Java内存模型中的重排序;4、happens-before原则;JMM相关的三个同步原语(synchronized,volatile,final)。
87 0
|
存储 缓存 安全
JAVA内存模型(JMM)详解
JAVA内存模型(JMM)详解
177 0
JAVA内存模型(JMM)详解
|
存储 算法 Java
|
存储 缓存 Java
Java内存模型(JMM)详解(2)
Java内存模型(JMM)详解
140 0
Java内存模型(JMM)详解(2)
|
存储 缓存 Java
Java内存模型(JMM)详解(1)
Java内存模型(JMM)详解
94 0
Java内存模型(JMM)详解(1)
|
缓存 Java 编译器
java内存模型
java内存模型
107 0
java内存模型
|
存储 缓存 安全
java内存模型(JMM)和happens-before
java内存模型(JMM)和happens-before
java内存模型(JMM)和happens-before