Java内存模型

Java内存模型

一、CPU内存模型(重点:多级缓存和乱序执行优化)

高速缓存:作为内存和处理器之间的缓冲,解决计算机的存储和处理器之间的运算速度差别大而导致CPU等待的问题。当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。

img

缓存一致性

当多个CPU的运算任务都涉及同一块主内存区域时,将可能导致各自的缓存不一致。如果真发生这种情况,那同步回到主内存时以谁的缓存数据为准呢?

缓存行:CPU缓存存储数据的最小单位,大小为64B

解决方案:

  1. 通过在总线加LOCK#锁的方式:因为CPU和其他部件进行通信都是通过总线来进行的,如果对总线加LOCK#锁的话,也就是说阻塞了其他CPU对其他部件访问(如内存),从而使得只能有一个CPU能使用这个变量的内存
  2. 通过缓存一致性协议(如MESI协议):保证了每个缓存中使用的共享变量的副本是一致的。核心思想:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的(嗅探),那么它就会从内存重新读取

image.png

CPU乱序执行优化

CPU为了使内部运算单元尽量充分利⽤,可能会对输⼊代码进⾏乱序执⾏优化

二、Java内存模型(JMM)

Java内存模型和CPU内存模型基本类似,JVM定义的内存模型屏蔽了不同操作系统和硬件的内存访问差异,实现了在各平台都能达到一致的内存访问效果。【一次编译,到处运行】

定义:线程间通信的控制机制,定义了主内存和线程之间的抽象关系。Java内存模型有主内存,每一个线程都会有自己的一份工作内存,线程在执行计算的时候,会先从主内存中复制一份数据到自己的工作内存,计算完之后再将数据刷回主内存

img

JMM关于同步的规定:

  1. 线程解锁前,必须把共享变量的值刷新到主内存;
  2. 线程加锁前,必须读取主内存的最新值到自己的工作内存;
  3. 加锁解锁是同一把锁。

三、JMM解决原子性、可见性、有序性的问题

  1. 原子性:Java通过synchronized关键字实现(锁),原理是java提供的两个高级字节码指令monitorenter和monitorexist
  2. 可见性:保证多线程操作时变量的可见性,被其修饰的变量在被修改之后立即同步到主内存,其他线程在用被其修饰的变量之前再从主内存中刷新。Java中的volatile就是保障变量的可⻅性,除此之外synchronizedfinal也可以保证变量的可⻅性。
  3. 有序性:保证多线程之间操作的有序性,实现方式有所区别,volatile关键字会禁止指令重排;synchronized关键字保证同一时刻只允许一条线程操作