单例模式的双重检查成例问题

2007-11-13  籽藤 

今天在看Spring的资料时,提到Spring实现的两种设计模式――单态(单例)模式和工厂模式

由于之前没有接触过单例模式,便找了《java设计模式》学习下。

这一看不打紧,觉得这单例模式很有意思。

单例模式有以下的特点:

单例类只能有一个实例;

单例类必须自己创建自己的唯一的实例;

单例类必须给所有其他对象提供这一实例。



在上面的对象图中,有一个“单例对象”,而“客户甲”,“客户乙”,“客户丙”是单例对象的三个客户对象。可以看到,所有客户对象共享一个单例对象,而且从单例对象到自身的连接线可以看出,单例对象持有对自己的引用。        Windows回收站就是单例模式的应用。

现在要讲到重点了――双重检查成例

先来看看一段代码:

//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中使用的。

        事实上这种双重检查成例与单例模式并无直接的关系,由于很多C语言设计师在单例模式里面使用了双重检查成例,所以这次做法也被很多Java设计师效仿。

        在这里还是要提醒一句――双重检查成例在java语言里行不通!!!

 

 

 

 

397°/3973 人阅读/0 条评论 发表评论

登录 后发表评论