目录
一、有序性产生的原因
编译优化导致的指令重排带来有序性问题
有序性指定的是程序按照代码的先后顺序执行。而编译器为了优化性能,有时候会改变程序中语句的先后顺序,例如: 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;
}
文章讨论了Java编程中由于编译器优化可能导致的有序性问题,特别是在实现单例模式时,由于指令重排可能引发线程安全问题。通过一个双重检查锁定的单例模式示例,解释了在instance变量分配和初始化之间可能出现的重排,这可能导致未初始化的对象被多个线程访问。为了解决这个问题,文章提出使用volatile关键字来确保内存可见性和禁止指令重排,从而保证线程安全。
1398

被折叠的 条评论
为什么被折叠?



