Log4X

链路纵横
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

双重检查模式单例续(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   YYX  阅读(2153)  评论(16编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示