Log4X

链路纵横
双重检查模式单例续(DCL的代替方案)

昨天发了一篇 "关于双重检查锁定单例模式",论证了DCL在java是不可行的,极其原因。

最后我提到C#也一样不能使用DCL。

然后很多朋友指出,C#是支持DCL的。后来我的确在一份文档中看到说 .net2.0以上使用DCL是安全的。这里先对我之前的说法做一个修正。

不过无论如何,这说明DCL并不能在C#所有版本中都工作的很好。但是有一种模式,既能实现DCL的延迟加载的目标,又不需要任何同步,而且还是线程安全的。

这种模式的关键,就在于利用了jvm/CLR运行时 本身的类加载机制。让我们来看看代码

java:

 1 public class Singleton {
 2 
 3     private Singleton(){}
 4     
 5     private static class Nested{
 6         static Singleton instance = new Singleton();
 7     }
 8     
 9     public static Singleton getInstance(){
10         return Nested.instance;
11     }
12 }

 

C#:

 

 1 public class Singleton {
 2 
 3     private Singleton(){}
 4     
 5     private static class Nested{
 6                 //original wrong code:
 7         //static Singleton instance = new Singleton(); 
 8                 //fixed:
 9                 internal static Singleton instance = new Singleton();
10     }
11     
12     public static Singleton GetInstance(){
13         return Nested.instance;
14     }
15 }
16 

 

其实这两份代码完全一样 :)

这种模式,通过在单例类内部定义一个静态内部类,由内部类内持有自身的一个实例。

这是由于对于jvm/CLR运行时来说,类信息,包括类中的变量,都是第一次要用到这个类时才会加载,也就是所谓的“按需加载”。

在调用getInstance()方法之前,Nested这个内部类从来没有被用到,所以其中的类变量根本没有被初始化。当第一次调用getInstance()方法时,由于解释器遇到了一个没见过的Nested类,于是就尝试加载,同时初始化该类中的所有静态变量,这样就达到了延迟初始化instance变量的目的。

另外,为何使用内部类,而不是新定义一个 InstanceHolder类呢?因为内部类有两大特点是其他类无法替代的。

 - 它可以为私有。除了Singleton内部,可以保证没有其他任何代码能接触到Nested类。

 - 它可以访问外部类的私有变量和方法。就如前面代码所示,Nested类中依然能够调用外部类私有的构造方法。

这种模式虽然没有同步块,但线程安全性完全不用担心,它依赖于运行时对类加载的处理,由运行时保证了类初始化对于任何线程的可见性与原子性。

posted on 2008-09-28 09:26  YYX  阅读(2149)  评论(16编辑  收藏  举报