# Java内存模型

Java线程之间的通信由Java内存模型控制，JMM决定了一个线程对共享变量的写入何时对另一个线程可见。 从抽象角度来看，JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中，每个线程都有 一个私有的本地内存，本地内存中存储了该线程以读、写共享变量的副本。本地内存是JMM的一个抽象概念，并不真 实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。 JMM通过控制主内存与每个线程的本地内存之间的交互，来为Java程序员提供内存可见性保证。

## Tip

* 在命令式编程中，线程之间的通信机制有两种：共享内存和消息传递
* 同步是指程序中用于控制不同线程间操作发生相对顺序的机制

## 重排序

在执行程序时，为了提高性能，编译器和处理器常常会对指令做重排序

* 编译器优化的重排序

  > 编译器在不改变单线程程序语义的前提下，可以重新安排语句的执行顺序 JMM编译器重排序规则会禁止特定类型的编译器重排序
* 处理器重排序

  > JMM处理器重排序规则会要求所有Java编译器在生成指令序列时，插入特定类型的内存屏障指令 通过内存屏障指令来禁止特定类型的处理器重排序

  * 内存屏障类型
    * LoadLoad Barriers
    * StoreStore Barriers
    * LoadStore Barriers
    * StoreLoad Barriers
  * 指令级并行的重排序

    > 现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性，处理器可以改变语句对 应机器指令的执行顺序
  * 内存系统的重排序

    > 由于处理器使用缓存和读、写缓冲区，这使得加载和存储操作看上去可能是乱序执行

JMM属于语言级的内存模型，它确保在不同的编译器和不同的处理器平台之上，通过禁止特定类型的编译器重排序和处理器 重排序，为程序员提供一致性内存可见性保证。

## happens-before

在JMM中，如果一个操作执行结果需要对另一个操作可见，那么这两个操作之间必须要存在happens-before关系。 这里提到的两个操作既可以是在一个线程之内，也可以是在不同线程之间。

## as-if-serial

* 语义

  > 不管怎么重排序（编译器和处理器为了提高并行度），（单线程）程序的执行结果不能被改变
* as-if-serial语义保证单线程内程序的执行结果不被改变，happens-before关系保证正确同步的

  &#x20;  多线程程序的执行结果不被改变。
* as-if-serial语句给编写单线程程序的程序员创造了一个幻境：单线程程序是按程序的顺序来执行的

  ```
  happens-before关系给编写正确同步的多线程程序的程序员创造了一个幻境：正确同步的多线程程序是按
  happens-before指定的顺序来执行的。
  ```

## 数据依赖性

如果两个操作访问同一个变量，且这两个操作中有一个为写操作，此时这两个操作之间就存在数据依赖性。 编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。 这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作，不同处理器之间 和不同线程之间数据依赖性不被编译器和处理器考虑。

## 顺序一致性模型

顺序一致性内存模型是一个理论参考模型，在设计的时候，处理器的内存模型和编程语言的内存模型都会以顺序 一致性模型作为参照。

### 两大特性

* 一个线程中的所有操作必须按照程序的顺序来执行。
* （不管程序是否同步）所有线程都只能看到一个单一的操作执行顺序。在一致性内存模型中，每个操作都必须

  原子执行且立刻对所有线程可见。

### 同步程序的顺序一致性效果

* 顺序一致性模型中，所有操作完全按程序的顺序串行执行。在JMM中，临界区内的代码可以重排序（但JMM

  不允许临界区内的代码“逸出”到临界区之外，那样会破坏监视器的语义）。

### 未同步程序的执行特性

* 对于为同步或未正确同步的多线程程序，JMM只提供最小安全性：线程执行时读取到的值，要么是之前某个线程

  写入的值，要么是默认值（0，null,false)，JMM保证线程读操作读取到的值不会无中生有的冒出来。
* JMM不保证未同步程序的执行结果与该程序在顺序一致性模型中执行结果一致。
* 在两个模型中的差异
  * 顺序一致性模型保证单线程内的操作会按程序的顺序执行，而JMM不保证单线程内的操作会按程序的顺序执行
  * 顺序一致性模型保证所有线程只能看到一致的操作执行顺序，而JMM不保证所有线程能看到一致的操作执行顺序
  * JMM不保证对64位的long型和double型变量的写操作具有原子性，而顺序一致性模型保证对所有内存读、写

    操作都具有原子性

## volatile

## lock

## synchronized

## final

## JMM的设计

### 考虑因素

* 程序员对内存内存模型的使用。程序员希望内存模型易于理解，易于编程，程序员希望基于一个强内存模型来编写代码。
* 编译器和处理器对内存模型的实现。编译器和处理器希望内存模型对他们的束缚越少越好，这样它们就可以做尽可能多

  的优化来提高性能。编译器和处理器希望实现一个弱内存模型。

### happens-before要求禁止的重排序

* 对于会改变程序执行结果的重排序，JMM要求编译器和处理器必须禁止这种重排序。
* 对于不会改变执行结果的重排序，JMM对编译器和处理器不做要求（JMM允许这种重排序）

## Java内存模型综述

顺序一致性内存模型是一个理论参考模型，JMM和处理器内存模型在设计时会以顺序一致性内存模型为参照。在设计时，JMM和处理器内存模型会对顺序一致性模型做一些放松，因为如果完全按照顺序一致性模型来实现处理器和JMM，那么很多的处理器和编译器优化都要被禁止，这对执行性能将会有很大的影响。 JMM屏蔽了不同处理器内存模型的差异，它在不同的处理器平台之上为Java程序员呈现了一个一致的内存模型。

## 各种内存模型之间的关系

JMM是一个语言级的内存模型，处理器内存模型是硬件级的内存模型，顺序一致性内存模型是一个理论参考模型。 常用的四种处理器内存模型比常用的三种语言内存模型要弱，处理器内存模型和语言内存模型都比顺序一致性内存模型要弱。 同处理器内存模型一样，越是追求执行性能的语言，内存模型设计得会越弱。

## JMM的内存内可见性保证

* 单线程程序。单线程程序不会出现内存可见性问题。编译器、runtime和处理器会共同确保单线程程序的执行结果与该

  程序在顺序一致性模型中的执行结果相同。
* 正确同步的多线程程序。正确同步的多线程程序的执行将具有顺序一致性(程序的执行结果与该程序在顺序性一致性内存模型中的执行结果相同)。这是JMM关注的重点，JMM通过限制编译器和处理器的重排序为程序员提供内存可见性保证。
* 未同步、未正确同步的多线程程序。JMM为它们提供了最小安全性保障：线程执行时读取到的值，要么是之前某个线程写入的值，要么是默认值（0，null,false)，JMM保证线程读操作读取到的值不会无中生有的冒出来。

## 总结

### Problem

* 介绍一下JMM

  > Java线程之间的通信由Java内存模型控制，JMM决定了一个线程对共享变量的写入何时对另一个线程可见。 JMM是一个语言级的内存模型，它是对顺序一致性模型的放松，它确保在不同的编译器和不同的处理器平台之上，通过禁止特定类型的编译器重排序和处理器重排序，为程序员提供一致性内存可见性保证，JMM确保正确同步的多线程程序的执行将具有顺序一致性（程序的执行结果与该程序在顺序性一致性内存模型中的执行结果相同）。对于未同步、未正确同步的多线程程序。JMM为它们提供了最小安全性保障：线程执行时读取到的值，要么是之前某个线程写入的值，要么是默认值（0，null,false)，JMM保证线程读操作读取到的值不会无中生有的冒出来。
