深入理解JUC02
Volatile关键字
为什么要使用volatile关键字,它有什么作用?
首先我们需要了解volatile的作用,volatile其实解决的就是上一节讲到的可见性和有序性的问题。 也就是说被volatile修饰的数据在多线程情况下是对其他线程可见的,并且编译器和处理器不能对其进行重排序(但这只是相对的,后面我们会讲到)
为什么volatile可以保证可见性和有序性呢?
保证可见性
JMM规定当某个线程修改了被volatile修饰的数据时,其他引用了该数据的线程需要重新从主存中访问该数据。其底层实现就是,当某个CPU修改了某个volatile数据并写入缓存时,数据在总线上传输的时候会被其他CPU监听到,CPU缓存中对应的数据就会失效。(感觉还是有概率发生线程安全问题,比如两个CPU同时操作某数据,同时在总线上传输,不过概率很小)需要注意的是volatile只能保证可见性,不能保证原子性,比如:
1 | volatile int i = 0; |
以上代码依然会发生线程安全问题,因为i++底层是由三个操作组成,虽然每个操作都是原子的,但组合在一起就不是原子的了。
保证有序性
volatile通过内存屏障来保证指令的有序性,可以禁止屏障内的指令和屏障外的指令重排序。但需要注意的是屏障内部和屏障外部的指令还是可以被重排序。
1 | A操作 |
即使加了内存屏障,执行顺序依然可以是E、(D、B、C)、A
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 dch'blog!