一、cpu的缓存模型
简化版
二、JMM内存模型
Java多线程内存模型跟cpu缓存模型类似,基于cpu缓存模型来建立的
1、数据原子操作
(1)read(读取)
从主内存读取数据
(2)load(载入)
将主内存读取到的数据写入工作内存
(3)use(使用)
从工作内存读取数据计算
(4)assign(赋值)
将计算好的值重新赋值到工作内存中
(5)store(存储)
将工作内存数据写入主内存
(6)write(写入)
将store过去的变量值赋值给主内存中的变量
(7)lock(锁定)
将主内存变量加锁,标识为线程独占状态
(8)unlock(解锁)
将主内存变量解锁,解锁后其他线程可以锁定该变量
2、缓存一致性协议(MESI)
多个cpu从主内存读取同一个数据到各自的高速缓存,当其中某个cpu修改了缓存里的数据,该数据会马上同步回主内存,其他cpu通过总线嗅探机制可以感知到数据的变化从而将自己缓存里的数据失效
3、缓存加锁
缓存锁的核心机制是基于缓存一致性协议来实现的,一个处理器的缓存回写到内存会导致其他处理器的缓存失效,IA-32和Intel 64处理器使用MESI实现缓存一致性协议
三、volatile底层原理
1、主要通过汇编lock前缀指令,它会锁定这块内存区域的缓存(缓存行锁定)并回写到主存,IA-32和Intel64架构软件开发者手册对lock指令的解释
(1)会将当前处理器缓存行的数据立即写回到系统内存
(2)这个写回内存的操作会引起其他cpu里缓存了该内存地址的数据无效(MESI协议)
(3)提供内存屏障功能,使lock前后指令不能重排序
2、指令重排序原则
(1)as-if-serial
不管怎么重排序(编译器和处理器为了提高并行度),单线程程序的执行结果不能被改变。编译器和处理器不会对存在数据依赖关系的操作做重排序,因为会改变执行结果。
(2)happens-before
只要sychronized和volatile关键字来保证原子性、可见性和有序性。从jdk5开始提供happens-before原则来辅助程序执行的原子性、可见性和有序性。
1)程序顺序原则
一个线程内必须保证串行。
2)锁规则
解锁操作必然发生在后续的同一个锁的加锁之前。即对应同一把锁只有先解锁才能加锁
3)volatile规则
volatile变量的写,先发生于读,volatile变量在每次被线程访问时,都强迫从主内存中读取该变量的值,而当变量发生变化时,又会强迫将最新的值刷新到主内存。
4)线程启动规则
线程的start方法先于它的每一个动作,即如果线程A在执行线程B的start方法之前修改了共享变量的值,那么当线程B执行start方法时,线程A对共享变量的修改对线程B可见
5)传递性
A先于B,B先于C,那么A先于C
6)线程终止规则
线程的所有操作先于线程的终结,Thread.join()方法的作用是等待当前执行的线程的终止。假设在线程B终止之前,修改了共享变量,线程A从线程B的join的成功返回后,线程B修改了共享变量对线程A是可见的。
7)线程中断规则
线程intrrupt()方法的调用先于发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()来检测线程是否中断
8)对象终结规则
对象的构造函数执行,结束先于finalize()aa方法
3、jvm内存屏障
不同cpu对于jvm内存屏障的实现指令不一样,拿Intel cpu来看实现的指令
(1)Ifence
是一种load barrier读屏障,实现LoadLoad屏障
(2)sfence
是一种store barrier写屏障,实现StoreStore屏障
(3)mfence
是一种全能屏障,具备Ifence和sfence的能力,实现所有屏障的能力