持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
二、内存屏障
写在前面
- volatile关键字可以保障线程间的可见性、多核情况下防止指令重排
- volatile通过内存屏障来防止指令重排。JVM在编译volatile关键字修饰的变量时,会加入特定的内存屏障来保证;
JMM 内存屏障主要有 Load 和 Store 两种
- loadBarrier 读屏障
- 在读指令之前插入读屏障,让缓存数据失效,如需使用再次从主存读取
- StoreBarrier:写屏障
- 在写指令之后,把写完之后的数据刷回主存
内存屏障的一般操作
-
StoreStore 屏障:
-
在每个volatile写操作前面加入StoreStore 屏障,保证按照顺序吧数据从缓存刷新回主存
l例如:A,StoreStore,B意味着在B写入操作执行之前,必须先保证A的写入操作先执行完,且写入结果对其他线程可见,禁止StoreStore两边的操作位置乱序;
-
-
StoreLoad屏障: A,StoreLoad,B
- 用于在数据读取操作之前,必须完成对数据的写入操作,保证A的写入必须在B读取之前,且A的写入结果对其他处理器可见;
-
LoadLoad屏障:
- 用于保证对两个读取的顺序保证A读取完成B才能读取,缓存中的数据失效,必须重新从主存中读取;
-
LoadStore屏障
- 执行在写入操作之前,保证前面的先读取完,才能执行后面的写入;
volatile 写操作的内存屏障
一般而言volatile写操作的内存屏障是 :
在每个 volatile 写操作前插入 StoreStore屏障,在写操作后面插入 StoreLoad 屏障。
SS写屏障的作用为:
----------->SS , volatile写 ,SL<-----------------
两端的指令不会发生乱序问题
在前面的写指令完成之后, 处理器的缓存行数据刷入主存
volatile前面的写操作,一定先执行。因为在 volatile 写之前,前面的所有普通写操作已经被刷新到主存。
StoreLoad 屏障作用
- 前面的写入操作会先执行完成,刷内完成;
- 前面的写指令完成之后,主存数据刷新完成
- 后面的读操作,让处理器缓存中的数据失效,重新从主内存加载数据
- 在 volatile 写完成之后,缓存中的已经刷新成最新数据,线程间可见。
volatile 读操作的内存屏障
volatile 读操作的内存屏障插入策略:
在每个 volatile 写操作后插入 LoadLoad屏障和 LoadStore 屏障
LoadLoad 屏障可以保证
前面的读操不会被排到后面去了;
让高速缓存中的数据失效,重新从主内存加载数据;
后面读操作不会被排到前面去了 ,因此在 volatile 读之后,所高速缓存中的数据是重新从主存加载的,并且是最新数据。
LoadStore 屏障可以保证:
- 前面的读操不会被排到后面去了;
- 让高速缓存中的数据失效,重新从主内存加载数据;
- 后面写操作不会被排到前面去了。
- 在 volatile 读之后,所高速缓存中的数据是重新从主存加载的,并且是最新数据‘
【参考总结于】Java高并发核心编程