volatile 实现原理了解吗?
volatile 有两个作用,保证可见性和有序性。
volatile 怎么保证可见性的呢?
简单来说:读取和写入变量从原本的本地内存变成主内存中
相比 synchronized 的加锁方式来解决共享变量的内存可见性问题,volatile 就是更轻量的选择,它没有上下文切换的额外开销成本。
volatile 可以确保对某个变量的更新对其他线程马上可见,一个变量被声明为 volatile 时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存 当其它线程读取该共享变量 ,会从主内存重新获取最新值,而不是使用当前线程的本地内存中的值。
当对volatile变量进行写操作的时候,JVM会向处理器发送一条lock前缀的指令,将这个缓存中的变量回写到系统主存中。 所以,如果一个变量被volatile所修饰的话,在每次数据变化之后,值都会被强制写入主存。而其他处理器的缓存由于遵守缓存一致性协议,就会把变量的值从主存读取到自己的工作内存中。这就保证了volatile在并发编程中,其值在多个缓存中是可见的
例如,我们声明一个 volatile 变量 volatile int x = 0,线程 A 修改 x=1,修改完之后就会把新的值刷新回主内存,线程 B 读取 x 的时候,就会清空本地内存变量,然后再从主内存获取最新值。
volatile内存可见性
volatile 怎么保证有序性的呢?
简单来说:禁止指令重排,增加读写相关屏障
重排序可以分为编译器重排序和处理器重排序,valatile 保证有序性,就是通过分别限制这两种类型的重排序。
volatile重排序规则表
-
volatile读之后的任何操作不能重排序到volatile读之前(对应第二行)
-
这么一个场景,需要根据volatile读出来的数据,进行后续的普通读写操作,若提前了,volatile数据是旧数据,导致语义错误。
-
volatile读之前的任何操作,就肯定不是基于该volatile变量的相关普通操作,所以不需要做相关屏障
-
-
volatile写之前的任何操作不能重排序到volatile写之后(对应第三列)
-
这么一个场景,普通读写操作完成后才能更改volatile写操作,volatile写提前,说明还没完成操作你就改。
-
-
volatile写之后的volatile相关操作不能重排序到前面
-
相当于对该volatile修饰的变量加锁
-
为了实现 volatile 的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
-
在每个 volatile 写操作的前面插入一个
StoreStore
屏障 -
在每个 volatile 写操作的后面插入一个
StoreLoad
屏障 -
在每个 volatile 读操作的后面插入一个
LoadLoad
屏障 -
在每个 volatile 读操作的后面插入一个
LoadStore
屏障
-
巧记:拆分成两个单词,读用Load,写用Store。LoadLoad读读,LoadStore读写·······
volatile写插入内存屏障后生成的指令序列示意图
volatile读插入内存屏障后生成的指令序列示意图
标签:线程,屏障,变量,了解,volatile,内存,原理,排序 From: https://blog.csdn.net/qq_62097431/article/details/142302373