Volatile关键字

为什么要使用volatile关键字,它有什么作用?

​ 首先我们需要了解volatile的作用,volatile其实解决的就是上一节讲到的可见性和有序性的问题。 也就是说被volatile修饰的数据在多线程情况下是对其他线程可见的,并且编译器和处理器不能对其进行重排序(但这只是相对的,后面我们会讲到)

为什么volatile可以保证可见性和有序性呢?

保证可见性

​ JMM规定当某个线程修改了被volatile修饰的数据时,其他引用了该数据的线程需要重新从主存中访问该数据。其底层实现就是,当某个CPU修改了某个volatile数据并写入缓存时,数据在总线上传输的时候会被其他CPU监听到,CPU缓存中对应的数据就会失效。(感觉还是有概率发生线程安全问题,比如两个CPU同时操作某数据,同时在总线上传输,不过概率很小)需要注意的是volatile只能保证可见性,不能保证原子性,比如:

1
2
3
4
volatile int i = 0;
public void add(){
i++;
}

以上代码依然会发生线程安全问题,因为i++底层是由三个操作组成,虽然每个操作都是原子的,但组合在一起就不是原子的了。

保证有序性

​ volatile通过内存屏障来保证指令的有序性,可以禁止屏障内的指令和屏障外的指令重排序。但需要注意的是屏障内部和屏障外部的指令还是可以被重排序。

1
2
3
4
5
6
7
8
9
A操作

内存屏障
B操作
C操作
D操作
内存屏障

E操作

即使加了内存屏障,执行顺序依然可以是E、(D、B、C)、A