设计模式---单例模式
2014-11-09 20:40 周信达 阅读(227) 评论(0) 编辑 收藏 举报前言
单例也是被嚼烂了的设计模式之一,但是这一模式在实际中确实使用非常广泛,今天,使用多个版本的单例模式实现,来讲一下实现单例需要注意的一些地方
版本1
使用静态字段,使用单例方法(属性)进行获取,第一次访问时进行初始化,以后直接返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public sealed class Singleton { // 私有构造函数,禁止外部访问 private Singleton() { } // 静态实例字段 private static Singleton _instance; // 静态实例方法 public static Singleton Instance { get { if (_instance == null ) _instance = new Singleton(); return _instance; } } } |
版本2
OK,单例方法的设计需要考虑多线程调用,所以线程同步是必须考虑的,否则可能就不是真的“单例”了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public sealed class Singleton { // 私有构造函数,禁止外部访问 private Singleton() { } // 静态实例字段 private static volatile Singleton _instance; private static readonly object syncRoot = new object (); // 静态实例方法 public static Singleton Instance { get { lock (syncRoot) { if (_instance == null ) _instance = new Singleton(); return _instance; } } } } |
1 |
其实在实际使用中,这个版本已经OK了,但是线程同步还可以做一点小小的优化,于是
版本3
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 | public sealed class Singleton { // 私有构造函数,禁止外部访问 private Singleton() { } // 静态实例字段 private static volatile Singleton _instance; private static readonly object syncRoot = new object (); // 静态实例方法 public static Singleton Instance { get { if (_instance == null ) { lock (syncRoot) { if (_instance == null ) _instance = new Singleton(); } } return _instance; } } } |
OK,现在这个版本已经完美了,可以使用了,而且线程同步进行了优化,不用每一次调用都进行lock操作,但是等等,很多人还有另一种风格,就是在静态字段中提供内联初始化或者默认初始化,然后让属性或单例方法直接返回该静态字段。因为使用静态字段的初始化语法,其实可以保证线程安全(CLR是这么处理的),所以不用我们自己去编写线程同步代码
版本4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public sealed class Singleton { // 私有构造函数,禁止外部访问 private Singleton() { Console.WriteLine( "Singleton Constructed" ); } // 静态实例字段 private static readonly Singleton _instance = new Singleton(); // 静态实例方法 public static Singleton Instance { get { return _instance; } } } |
1 |
OK,现在不用在获取实例的方法(属性)中写构造表达式了,更不用自己写线程同步的代码了,但是现在有一个问题,那就是字段的内联初始化的初始化时间是提前的,而且是不确定的,因为没有提供默认静态构造函数的话,静态字段的内联初始化会生成beforeInit标记,字段的初始化会在使用类型以前随机寻找一个时间来调用,这可以适度优化
版本5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public sealed class Singleton { // 私有构造函数,禁止外部访问 private Singleton() { Console.WriteLine( "Singleton Constructed" ); } // 默认静态构造函数 static Singleton() { } // 静态实例字段 private static readonly Singleton _instance = new Singleton(); // 静态实例方法 public static Singleton Instance { get { return _instance; } } } |
看到区别了么?现在只是加了一个静态构造函数,为了防止内联过早的进行调用,现在不会生出fieldBeforeInit信息,所以会到调用之前才进行初始化。但是关于调用的时机,是不是依然可以优化,优化到真正调用时才进行初始化呢?好了,请看下一个版本
版本6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Singleton { // 私有构造函数,禁止外部访问 private Singleton() { Console.WriteLine( "Singleton Constructed" ); } public static Singleton Instance { get { return NestedObject.NestedInstance; } } private class NestedObject { static NestedObject() { } public static readonly Singleton NestedInstance = new Singleton(); } } |
这个版本使用内嵌的私有类作为一个容器来维持单例对象,使用静态初始化的方式并且支持延迟初始化,非常聪明。那么是否到此为止呢?其实对于静态初始化的延迟执行,.NET提供了非常方便的Lazy类,我们何不拿来一用,这样我们就不用自己编写内部类了
版本7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Singleton { private static Lazy<Singleton> _instance = new Lazy<Singleton>(() => new Singleton()); // 私有构造函数,禁止外部访问 private Singleton() { Console.WriteLine( "Singleton Constructed" ); } public static Singleton Instance { get { return _instance.Value; } } } |
总结
大家对单例模式的说法一向都是简约不简单,所以此模式虽然已被嚼烂,但它依然是作为程序员的必修课之一,这里面考察的主要是
- 类的修饰符,一般建议使用 sealed,密封类,不允许继承
- 类的构造函数,一般建议private,不允许外部访问
- 类的执行时机,根据实际需求判断,是否饿汉式初始化或者惰性初始化
- 类的初始化线程安全的考虑,必须考虑多线程访问时,构造仅执行一次
实践中,可用的方式包括,版本3(单例方法初始化,加双检查锁定,延迟初始化)、版本4、5(静态初始化,非延迟初始化)、版本6、7(静态初始化,延迟实例化),其实都是可以使用的,具体选择哪个没有标准的,根据实际应用场景来选择吧
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步