volatile的可见性和有序性
2019-12-11 本文已影响0人
Brook_liu
什么是volatile的可见性?
首先要从JMM内存模型说起,每一个线程都会有自己的工作内存,而线程要访问主内存的共享变量时,是会将变量对象拷贝一份变量副本到工作内存。
对于普通共享变量,线程读取的变量值是工作内存的副本,修改后并非立即写入主内存,因此在多线程并发的情况,一个线程修改了变量值后,另一个线程可能依然读取的是自己工作内存的变量副本,而没有读到另一个线程修改后的变量值。因此就会造成并发情况下出现逻辑控制上的问题。
对于volatile修饰的变量,线程在修改工作内存的变量副本时,会置其他线程工作内存中该变量副本为无效,并在修改后立即写入主内存。而其他线程在发现工作内存中变量副本置为无效时会去主内存读取最新的值,这个过程会去等待修改线程先写入主内存。因此volatile修饰变量后,线程的读写效率会降低。
volatile修饰变量后,可能引入的问题:因为volatile修饰后,变量修改后会立即写入到主内存。所以可能存在一个线程内已经对该变量判空,但是因为另一个线程置空,导致本线程更容易发生空指针异常。
volatile A a = new A();
线程1
a = null;
线程2
if(a != null){
a.b();// 可能空指针异常
}
什么是volatile的有序性?
说到有序性就不得不说说指令重排序,什么是指令重排序?简单的说就是系统为了优化代码执行效率,会打乱代码先后顺序并可能先执行后面的代码。
举个例子:
int a=1; // 代码1
int b =2;// 代码2
int c =a+b; // 代码3
代码2可能会比代码1先执行,但是代码3不会比前面的先执行,因为这样会出错,代码3依赖前两者的结果。所以系统在保证逻辑正确的情况下会对代码重排序。
而volatile修饰的变量就可以禁止指令重排序。
举个例子:
//变量c是volatile类型
int a =1;//代码1
boolean flag =true; //代码2
c =2;//代码3
a =2;//代码4
flag =false; //代码5
代码1和代码2可能重排序,代码4和代码5可能重排序,但是代码3不会先于代码1和2执行,也不会在代码4和5之后执行。