作用
volatile关键字是一个很老的关键字了,并不只是在java中有的,在C语言中也有,volatile关键字在java的作用就是保护多线程环境下的可见性和有序性。
可见性简单说就是线程a对公共数据的修改对线程b来说是可知的。
有序性是这段代码的执行顺序在底层编译、执行的时候不会被打乱执行。
如何实现
可见性 :
当变量声明为volatile时编译后多出这么一行
0x00bbacde: lock add1 $0x0,(%esp);
意思是给值做加0操作,并且这个操作前面有lock前缀。
lock的作用:老处理器,对传输数据总线进行锁定,其他处理器无法使用总线。使得只用这个总线可以读取数据所在的内存区。这种方法就会导致效率低下。
后来的处理器:不再锁总线,而是会检查数据所在的内存区域,如果是在线程缓存中,则锁定该区域,写完后写回主存,并利用缓存一致性协议保证其他线程可见。
缓存一致性协议
线程处理器会检测总线的内存中的内存地址被其他线程工作的情况,如果有其他线程要修改,并且该内存地址也在自己的缓存中的话就会强制使自己的缓存中的地址无效。所以当该处理器要访问该数据的时候,由于发现自己缓存的数据无效了,就会去主存中访问。
这也就是
(1)当写一个volatile变量时,JMM会把该线程本地内存中的变量强制刷新到主内存中去;
(2)这个写操作会导致其他线程中的volatile变量缓存无效。
有序性:
通过内存屏障实现限制处理器的重排序。
硬件层面,内存屏障分两种:读屏障(Load Barrier)和写屏障(Store Barrier)。内存屏障有两个作用:
阻止屏障两侧的指令重排序;
强制把写缓冲区/高速缓存中的脏数据等写回主内存,或者让缓存中相应的数据失效。
在生成字节码时插入。
原子性:
volatile不能保证原子性操作。