我们都知道,volatile关键字在Java中的作用,是用来实现内存可见性
与禁止重排序
的。
可是,为什么需要
内存可见性
?为什么需要禁止重排序
?
内存可见性
说到内存可见性,就需要提起Java内存模型,也就是JMM。
JMM的设计类似于CPU与CPU缓存,JVM中处理的数据不是直接去操作堆中的数据,而是每个线程都会有自己独有的内存空间,线程将堆中需要的数据在该空间内创建自己的副本,使用完之后再修改堆中的值。
volatile boolean flag = true;
public static void main(String[] args){
Test t = new Test();
new Thread(() -> t.flag = false).start();
new Thread(() -> {
while (t.flag) {
}
}).start();
}
思考以下序列
- 线程A读取flag=true副本得flagA=true
- 线程A将flagA=false
- 线程B读取flag=true副本得flagB=true
- 线程A将flag=false写回给主内存
- 线程B看不到线程A更新的新值,将永远while(true)下去
而volatile关键字,将会给flag这个变量添加一个内存屏障,该内存屏障用来保障变量的可见性。
禁止重排序
我们的代码,在进行编译时,会进行一些优化,其中的一种情况就是,在单线程环境下,调整语句的顺序不会对程序运行的结果产生影响,这时候编译器可能就会对语句的执行顺序进行调整。这种情况在多线程环境下,可能就会产生错误。而volatile则禁止进行指令重排序,也就是happens-before中的一条:
对volatile修饰的字段的写操作,happens-before与任意后续对这个字段的读操作
volatile是锁吗
volatile虽然可以在同步相关的一些工作上帮助我们,但是它并不是锁,主要还是因为它并不能保证原子性