JMM 内存模型

文章目录

    • JMM
      • 主内存、工作内存、内存交互
      • 内存模型的三大特性
      • 指令重排序
      • as-if-serial语义
      • happens-before原则


JMM

JMM:JavaMemoryModel,即 Java 内存模型,用于屏蔽各种硬件和操作系统的内存访问差异。
注:
JMM 和 JVM内存结构不是一个层次的划分:

  • JVM 内存结构是伴随JVM的启动而对内存区域进行的划分是真实存在的
  • JMM 只是一个抽象的概念,和多线程相关的,描述了一组规则或规范,这些规则或规范围绕如何处理并发过程中的原子性、可见性和有序性来建立的。

主内存、工作内存、内存交互

内存模型结构
JMM 内存模型_第1张图片

  • 所有的变量都存储在主内存中,每个线程都有自己的工作内存,而工作内存则存储在 CPU 的高速缓存或寄存器中(比在内存中读写快)。
  • 每个线程只能直接操作自己工作内存中的变量(由主内存拷贝而来的或局部变量),不同线程之间的变量值的传递需要通过主内存。

内存间的交互
8个操作:
JMM 内存模型_第2张图片

  1. read:把一个变量值从主内存传到工作内存中
  2. load:把 read 到的值放入到工作内存的变量副本中
  3. use:把工作内存中的一个变量值传给执行引擎
  4. assign:把一个执行引擎返回的值传给工作内存
  5. store:把工作内存的一个变量值传到主内存中
  6. write:把 store 到的值放到主内存的变量中
  7. lock、unlock:作用于主内存的变量,锁定与释放

操作的使用规则:

  1. store 和 write、read 和 load 不会单独出现,但之间可以插入其它指令。
  2. assign 之后必须将工作内存中的值同步回主内存,且未 assign,则不能进行同步。
  3. 新变量都在主内存中生成,工作内存中的变量必须经过 load。
  4. lock 和 unlock 必须成对出现且 lock 必须先执行,lock 一个主内存中的变量时会清空工作内存中的此变量的值,需要重新 load。

内存模型的三大特性

也是并发编程的三个特性:

  • 原子性
    Java 内存模型保证了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性。
    但只是单个操作具有原子性,当用多线程对同一变量进行以上多个操作时依然会出现线程安全问题,我们可以一样得使用 AtomicInteger 或 sychronized 来保证操作的原子性。

  • 可见性
    Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。
    JMM提供了volatile变量定义、final、synchronized块来保证可见性。

  • 有序性
    有序性是指在本线程内观察所有操作是有序的,而对另一个线程来说,此线程所有操作是无序的,因为发生了指令重排序
    Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序不会影响单线程程序的执行,却会影响多线程并发执行的正确性。
    注: volatile 也可以通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障前面。或者通过 synchronized 来保证有序性


指令重排序

概述
重排序的作用:提高程序执行时的性能。
分为两类:

  • 编译器重排序:编译器优化的重排序
  • 处理器重排序:指令集并行的重排序、内存系统的重排序

排序的顺序为:编译器优化重排序->指令集并行重排序->内存系统重排序->得到最终的指令序列


as-if-serial语义

编译器处理器都必须遵守,即保证不管怎么重排序,程序的执行结果不会被改变(不会对由数据依赖关系的操作做重排序)。as-if-serial语义使程序员不必担心单线程中重排序的问题干扰他们,也无需担心内存可见性问题.
数据依赖性:

		int a = 1;
		int b = 2;
		int c = a+b;

a 和 c 之间存在数据依赖关系,b 和 c 之间也存在数据依赖关系,而a和b之间就没有。所以可以重排序 a 和 b。
注: 只作用于单线程中的数据依赖,而多线程中即使存在数据依赖,也可能会改变程序的执行结果。


happens-before原则

Happens-before,即先行发生原则,可以保证多线程中发生指令重排也不会改变程序的执行结果,以保证有序性
主要规则如下:

  • 程序顺序规则:一个线程中的每个操作,happens-before于线程中的任意后续操作。
  • 监视器锁规则:对一个锁的解锁(unlock ),总是 happens-before 于随后对这个锁的加锁(lock)。
  • volatile变量规则:对 volatile 变量的写,happens-before 于任意对这个变量的读。
  • 传递性:如果A happens-before B,且Bhappens-before C,那么Ahappens-before C。

注: happens-before关系是要求前一个操作的执行结果对后一个操作可见,并不是必须在它之前执行。

你可能感兴趣的:(并发编程)