java并发编程 - volatile关键字

214 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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的入栈出栈知识