理解JMM
什么是JMM?
- JMM(Java Memory Model)就是Java内存模型,是Java虚拟机规范中所定义的一种内存模型。因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的程序运行在不同的系统上会出现各种问题。因此Java内存模型屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证一致的并发效果。
- JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。
- Java 内存模型规定了如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。
![image]()
Java内存模型和Java内存区域区别:
- Java内存区域:JVM运行时将数据分区域存储,简单的说就是不同的数据放在不同的地方。通常又叫做运行时数据区域。
- Java内存模型(JMM):定义了程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。
JMM规定:
- 所有的共享变量都存储于主内存。这里所说的变量指的是实例变量和类变量,不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题;
- 每一个线程还存在自己的工作内存,线程的工作内存,保留了被线程使用的变量的工作副本;
- 线程对变量的所有的操作(读,写)都必须在工作内存中完成,而不能直接读写主内存中的变量;
- 不同线程之间也不能直接访问对方工作内存中的变量,线程间变量值的传递需要通过主内存中转来完成。
JMM的8种内存交互操作:

- 不允许read和load、store和write操作之一单独出现,也就是read操作后必须load,store操作后必须write。即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况出现;
- 不允许线程丢弃它最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存;
- 不允许线程将没有assign的数据从工作内存同步到主内存;
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。也就是对变量实施use和store操作之前,必须经过load和assign操作;
- 一个变量同一时间只能有一个线程对其进行 lock 操作。但 lock 操作可以被同一条线程重复执行多次,多次lock之后,必须执行相同次数unlock才可以解锁;
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值。在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值;
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量;
- 一个线程对一个变量进行unlock操作之前,必须先把此变量同步回主内存。
JMM的3大特征:
- 原子性
- 原子性是指一个操作是不可分割、不可中断的,要么全部执行成功要么全部执行失败。
- JMM只能保证对基本数据类型的变量的读写操作是原子性的,但long和double除外(long和double的非原子性协定)。
- 在并发环境下,为了保证原子性,Java提供了synchronized关键字。因此在synchronized 修饰的代码块之间的操作都是原子性的。
- 可见性
- 可见性是指所有线程都能看到共享内存的最新状态。即当一个线程修改了一个共享变量的值时,其他线程能够立即看到该变量的最新值。
- 对于可见性问题,Java是提供了一个volatile关键字来保证可见性。当一个共享变量被volatile关键字修饰时,这个变量被修改后会立即刷新到主内存,保证其他线程看到的值一定是最新的。除了volatile关键字之外,final和synchronized也能实现可见性。
- final关键字修饰的变量,在构造器中一旦初始化完成,如果没有对象逸出(指对象没有初始化完成就可以被别的线程使用),那么其他线程都就可以看见final修饰的变量。
- synchronized的原理是,线程进入synchronized代码块后,线程会获取到lock,将会清空本地内存,然后从主内存中拷贝共享变量的最新值到本地内存作为副本,执行代码后又将修改后的副本值刷新到主内存中,最后线程执行unlock。
- 顺序性
- 有序性是指程序执行的顺序按照代码的先后顺序执行。
- 在Java中,可以通过volatile和synchronized关键字来保证多线程之间操作的有序性。
- volatile关键字是通过在主存中加入内存屏障来达到禁止指令重排序,来保证有序性。
内存屏障:内存屏障,也称内存栅栏,内存栅障,屏障指令等, 是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。
- synchronized关键字原理是,一个变量在同一时刻只能被一个线程lock,并且必须unlock后,其他线程才可以重新lock,使得被synchronized修饰的代码块在多线程之间是串行执行的。


浙公网安备 33010602011771号