目录
一、有序性产生的原因
编译优化导致的指令重排带来有序性问题
有序性指定的是程序按照代码的先后顺序执行。而编译器为了优化性能,有时候会改变程序中语句的先后顺序,例如: a =6;b=7,编译器优化后可能编程了b=7;a=6。在这个例子里,编译器调整了语句的顺序,但不会影响到最终结果。
在java领域中经典案例利用双重检查创建单例对象,例如下面的代码:在获取实例的getInstance()方法中,我们先判断instance是否为空,如果为空则锁定Singletion.class并再次检查intance是否为空,如果还为空就创建一个Singleton的实例。
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance(){
// B
if(instance== null){
synchronized (Singleton.class){
if(instance== null){
// A , 开辟空间,instance指向地址,初始化
instance= new Singleton ();
}
}
}
return instance;
}
上面的代码有什么问题吗?看起来很完美,但是问题出现在instance= new Singleton ();语句,该语句会执行三个操作,1、开辟空间 2、初始化 3、instance指向地址
如果按照正常顺序来说也不会有问题,但是指令2和指令3之间不存在依赖关系,编译器优化后可能产生重排,执行顺序为1 --> 3 --> 2,那么这个时候可能会产生在A线程刚把instance指向对应地址后,B线程获取到执行权,然后获取到一个没有初始化的对象,从而产生空指针异常。A、B两个线程的执行顺序如下:
二、怎么解决上面的有序性问题
实际上我们只要解决了指令重排,就可以解决这个有序性问题。那么我们可以通过添加关键字volatile来解决,如下所示:
private static volatile Singleton instance;
private Singleton (){}
public static Singleton getInstance(){
// B
if(instance== null){
synchronized (Singleton.class){
if(instance== null){
// A , 开辟空间,instance指向地址,初始化
instance= new Singleton ();
}
}
}
return instance;
}