Featured image of post JVM 内存模型

JVM 内存模型

JMM

Java 内存模型(Java Memory Model)屏蔽了不同操作系统和各种硬件的内存访问差异,实现了 Java 程序在各种平台下,都能达到一致性的访问效果。

JMM

JMM 规定了:

  • 所有的变量都存储在主存中
  • 每条线程有自己的工作内存
  • 线程对变量的所有操作(读取、赋值等),不能直接读写主存中的数据,都必须在工作内存中进行

memory

原子性、可见性、有序性

Java 内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性三个特征来建立的。

🐥 原子性

一次或多次操作,要么全部执行且不受到任何干扰,要么全都不执行。

  • 利用锁,可以让任一时刻只有一个线程访问代码块。synchronized Lock
  • 利用 CAS 原子类,保证原子操作。

🐤 可见性

一个线程修改了共享变量的值,其他线程能够立即得知这个修改。

  • volatile

规定每次写操作,都立即同步到主存;每次读操作,都从主存中读取。

  • synchronized lock

对一个变量执行执行 unlock 之前,必须把变量同步回主存中。

  • final

🐣 有序性

指令重排问题,代码的执行顺序不一定。

  • volatile

包含禁止指令重排序的语义。

  • synchronized

只有一条线程能够进入临界区。

volatile

Java 虚拟机提供的轻量级内存同步机制。

作用:

  • ✔️ 保障了此变量对所有线程的可见性

volatile 的写操作,都会刷新到主内存中,并使其他线程中的 volatile 变量失效;volatile 的读操作,都会从主存中读取。

  • ✔️ 禁止指令重排优化(有序性)

防止写操作之前的代码,重排到写操作之后;防止读操作之后的代码,重排到读操作之前。

  • ❗️无法保障原子性

一条字节码在执行时,是需要运行多条指令才能实现。

实现方式:内存屏障。重排序时,不能把后面的指令重排序到内存屏障之前的位置。

保证:之前的指令一定全部执行,之后的指令一定都没有执行,并且前面语句的结果对后面的语句可见。

happens-before 原则

对于两个操作 A 和 B,这两个操作可以在不同的线程中执行。如果 A happens-before B(A 优先于 B 执行),那么可以保证,当 A 操作完后,A 的操作对于 B 操作是可见的。

指令重排提高了并发性能,但是 Java 虚拟机会对指令重排做一些规则限制,并不能让所有的指令都随意改变执行位置。

Java 内存模型天然的先行发生关系:

  • 程序顺序规则

    在一个线程内,前面的代码操作优于后面的代码操作。

  • 锁定规则

    一个 unlock 操作优于后面对于同一个锁的 lock 操作。

  • volatile 规则

    一个 volatile 变量的写操作,优于后面这个变量的读操作。

  • 传递规则

    如果 A happens-before B,且 B happens-before C,那么 A happens-before C。

  • 线程启动规则

    Thread 对象的 start() 方法 happens-before 线程的每一个动作。