`
Dapple
  • 浏览: 101039 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Singleton Pattern 单态模式

阅读更多
Singleton Pattern 单态模式。

这个模式较简单,就是为了保证一个类只有一个实例,用一个入口来获取该实例。

例子如下(代码来自维基):

class Foo {
    private static Helper helper = null;
    private Foo(){}
    public static Helper getHelper() {
        if (helper == null)
            helper = new Helper();
        return helper;
    }
 
    // other functions and members...
}

由于构造方法设为了私有,所以其它类无法使用new 来生成该类实例,只有通过
该类自己提供的方法来获取实例。这个获取实例的方法通总是使用第一次创建的
实例而保证其它类总是使用同一个实例。

本来很简单,但是在多线程情况下则会有些麻烦。两个线程都有可能执行到了第
6行,这样就有可能创建2个实例。过程如下(线程1简称为T1,线程2 简称T2):

T1 5 (5代表第5行)
T2 5
T1 6
T1 7 (线程1返回一个实例)
T2 6
T2 7 (线程2返回另一个实例)

这样就破坏了实例唯一性。如果这两个实例状态不会变化,倒也无所谓,就是多
花点内存养两个对象。但如果这两个实例涉及到计数器等状态变化,就埋下了隐患。

解决办法是加上同步关键字,如下:

    public static synchronized Helper getHelper() {
        if (helper == null)
            helper = new Helper();
        return helper;
    }


问题是解决了,但效率却降低了。因为每次获取实例都会执行同步运算。实际上
没必要。一旦实例创建并返回,以后再获取实例就不会执行第6行。为了发生几率
很小的事件而每次同步的方法,代价太大了。

不同虚拟机对同步方法的执行效率不同,早期虚拟机执行同步效率非常的低。现
在改进了很多,但进入线程体、离开线程体、加锁、解锁这些步骤仍然耗费性能。

所以,后来发明了双检查锁模式。(Double-Checked Locking Pattern)。在
《DesignPatternsExplained》一书中,作者专门分析了这种方法的优势,并给出
了代码。代码如下:

class USTax {
    private static USTax instance;
    private synchronized USTax(){};
    public static USTax getlnstance () {
if(instance== null)
     instance= new USTax(); 
return instance; 
    } 
}


不想,书上印错了,这让很多人包括我迷惑良久。后来书本经过更正,变为如下形式,

class USTax {
   private static USTax instance;
   private USTax(){};
   public static synchronized void doSyn(){
 if(instance== null){
     instance= new USTax();
     instance.setSomething();}//这句是我加的,为方便接下来的解释。
   }
   public static USTax getlnstance () {
 if(instance== null)
      doSyn();
 return instance; 
    } 
}


到此,算是万事大吉了。不料这种双重检查模式仍然有问题,并不能解决多线程
的问题。由于这本书的广泛流传,这种模式也广为人知。当这种模式的错误之处
被发现后,很快就出现了对这种模式广泛的讨伐之声。可见,影响越大,一旦有
错,被讨伐的力度也就越大。

我们以上面这段代码为例,看看问题是怎样产生的,

T1 10 (线程1执行到第10行)
T1 5
T1 6
T2 10
T2 12

也就是说,线程1还没有完全构造完这个对象(还没有执行第7行),线程2就返回
这个半成品对象了。所以,这种谬种流传的 Double-Checked Locking Pattern也
就寿终正寝,不能再被使用了。

那这种多线程出现的问题该怎么做呢,解决方法也很简单,如下即可,

class USTax {
   private static USTax instance = new USTax();
   private USTax(){};
   public static synchronized void doSyn(){
if(instance== null)
     instance= new USTax();
   }
   public static USTax getlnstance () {
return instance; 
    } 
}
0
2
分享到:
评论
1 楼 mercyblitz 2010-05-31  
貌似DLC还是有问题啊,在static字段那里需要加一个volatile.

相关推荐

Global site tag (gtag.js) - Google Analytics