双重检查成例在Java 编译器

《java与设计模式》中谈到了这样一种情况:在Singleton模式的使用中,存在一种错误的技巧,即“双重检查成例”,代码如下所示:

//Broken multithread version
public class ResourceObject {

 private static ResourceObject obj=null;
 //unnormal private constructor
 private ResourceObject()
 {}
 
     //double-check locking method
 public static ResourceObject getResourceObject()
 {
  if(obj == null)//first check position
  {
   //more than one thread will get here
   synchronized(ResourceObject.class)
   {
    //only one thread every single time here
    if(obj == null)//second check here
    {
     obj=new ResourceObject();
    }
   }
  }
  return obj;
 }
 //other functions and members
}

两次检查看似可以省去不必要的同步开销,但是对于java编译器来说这种技巧并不成立。原因在于,java编译器会优化生成的中间代码,导致 ResourceObject类的初始化与变量obj的赋值之间的顺序变得不可预料。在单线程的情况下,这不会导致程序错误,而且也没有同步问题。但是在 多线程的情况下,在前一个线程执行完赋值语句,然后进行剩下的初始化时,后一个线程就会获得未初始化完的对象实例,此时调用ResourceObject 类的方法,就有可能使用初始化过程中未初始化完的类成员,造成程序崩溃。鉴于这一切行为都是无法预测的,双重检查是不建议在java中使用的。

你可能感兴趣的:(java,设计模式,多线程,thread)