一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
volatile
volatile是JVM提供的一种轻量级同步机制。
volatile会调用CMPXCHG,增加LOCK前缀,LOCK的含义
1.对JMM模型加锁总线锁 / 缓存锁 ,根据CPU而定.如果增加了缓存锁,就会触发MESI (MESI是某些CPU对缓存锁的实现) .相当于volatile借助MESI来实现可见性
2.回写修改后的数据,让其他CPU相关缓存行失效,需要重新读取
2.在禁止该指令前后的指令重排序,即保证happends-before关系
为什么要使用volatile关键字
1..可见性. 2. 防止指令重排序 3.不保证原子性
可见性
汇编角度:关于volatile修饰的变量,在汇编中会增加LOCK 前缀。
嗅探机制: 根据嗅探机制,其他CPU能够发现LOCK指令写入的变量,将会将自己缓存中的该变量设置为I(Invalid),当其他CPU下次读到这个变量,就会重新从主存中读取.
实现流程:1.当对volatile变量进行修改后,会立即将该变量刷回主存中。其他CPU嗅探到了该变化,将自己缓存中的该变量设置为不可用。当下次需要用到该变量时,就会从主存中重新缓存到缓存行中。
因此,缓存一致性,汇编指令LOCK前缀,CPU嗅探 这三者的共同作用下实现了可见性
防止指令重排
由于使用了LOCK前缀,它具有类似于内存屏障的功能,来保证指令禁止重排
内存屏障代表CPU对该指令必须执行完才能去执行其他指令
-
volatile 写的前后会增加内存屏障,前面加storestore,后面加storeload
- storestore保证volatile 写前的所有写 zaivolatile 写之前刷新到主存
- storeload 保证volatile 写能立即刷新到主存,即对之后的语句可见
-
volatile读 的后面会增加内存屏障,loadload 和loadstore
- loadload 禁止 volatile 读和下面的读重排序
- loadstore 禁止volatile读和下面的写重排序
不保证原子性-单条指令可以
如果对变量的操作是单条指令字节码,那么可以保证原子性。因为单条指令的前后会增加内存屏障。
如果不是单条指令字节码,就不能保证原子性
非单条指令字节码例子
以i ++ 为例。 i++ 看似只是一个动作,但是它并不是单条指令
//1.读取i
i load
//2.对 i + 1操作
i add
//3. 将结果写回
i store
因此,对于i++这种操作来说,并不能保证它的原子性
继续深入会涉及到字节码和JVM的入栈出栈知识