volatile 内存可见性的再次解释

重排序的几种情况

1
2
3
4
1、Load Load
2、Load Store
3、Store Load # 关于 TSO 模型,只有 Store Load 才会发生重排序的现象
4、Store Store

Hotspot

在这里插入图片描述
把指令放入 local_dummy。而 local_dummy 又是 volatile 修饰,不会发生编译器指令重排,仅此而已。
asm 后面的volatile关键字 会阻止编译器对本条指令前后的指令重排序优化,这保证了CPU得到的指令流是符合我们程序的语意的。
在这里插入图片描述

避免不同线程命中相同缓存行
在这里插入图片描述

storeload 对变量先写后读,使用 lock 前缀。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

TSO

有 TSO缓存一致性,锁缓存,使用 lock 前缀,保证 FIFO。没有缓存一致性,锁总线。

volatile初始设定

在这里插入图片描述

内存划出一小块区域,叫做 hole(洞)。
当 cpu 访问 hole 的时候,我就当是做什么?读我的外设,而不是去读内存。
这个内存我划分出一小块,这个内存你是拿指令是读不到的,你拿内存地址是访问不到的,因为它映射到外设上面去了。

这个时候,volatile 就出来了。

cpu 内部有一个东西叫做存储单元,存储单元里面有什么?集群器组。

那么,如果说没有 volatile,编译器把我们的指令优化成,比如说 mov 一个指定的值,放到 eax寄存器里面进行使用 mov(指定) eax
如果说,没有用 volatile修饰变量,我们是单核处理器,它就会把 mov指令操作给优化掉,每次取的时候只会取 eax,后面你再用,它就是eax,因为它缓存。

因为有了 volatile,它会告诉编译器,别给我瞎优化好吗,别用寄存器(eax),每次都给我读地址,它这个时候就每次会去外设里面去看,去拿值。如果你的指令被缓存起来了,那么就会出问题。

这是 volatile 出现最原始的出现在单核处理器里面出现的,在 C语言里面出现的。就是告诉,编译器不要优化,不要做指令缓存,不要将我的值放到寄存器里面被缓存起来。

不是有MESI了吗?为什么还要 volatile?

MESI 用不用它都存在,java的volatile干一件什么事?CPU指令干预。
意味着,它会把 store buffer 刷出去,它会把 load buffer 刷出去。然后就应用了 MESI,你只要刷到 MESI里面,MESI就生效了,然后就锁缓存了,就不用锁总线了。

所以,你加 lock 前缀,或 MESI 就是干了这个事。