单例模式
简介
单例模式, 是一种常用的软件设计模式. 在它的核心结构中只包含一个被称为单例的特殊类. 通过单例模式可以保证系统中, 应用该模式的一个类只有一个实例. 即一个类只有一个对象实例.
特点
1, 单例类只能有一个实例.
2, 单例类必须自己创建自己的唯一实例.
3, 单例类必须给所有其他对象提供这一实例
实现
说到代码实现, 主要是 3 点
1. 私有静态变量
2. 私有化构造函数
3. 静态的对象获取方法
相信看到这的时候你心里就有个答案了, 看看和下面的所谓懒汉模式的代码是不是一样.
懒汉模式, 线程不安全
- /// <summary>
- /// 懒汉, 线程不安全
- /// </summary>
- public class Singleton1
- {
- private static Singleton1 _singleton = null;// 私有静态变量
- private Singleton1() { }// 私有化构造函数
- public static Singleton1 GetSingleton()// 静态的对象获取方法
- {
- if (_singleton == null)// 保证为空才创建
- {
- _singleton = new Singleton1();
- }
- return _singleton;
- }
- }
如果这个答案和你想的一样, 并一直也是这样用的, 那只能说 too young to simple, 此方法其实有俩方面的问题, 首先是线程不安全, 如果同一时间, 多个线程同时获取实例, 则会出现获取到不同的实例, 所谓的单例也就成了线程内单例, 明显不对, 解决办法往下看.
懒汉模式, 线程安全
- /// <summary>
- /// 懒汉, 线程安全
- /// </summary>
- public class Singleton2
- {
- private static Singleton2 _singleton = null;
- private static object _lock = new object();
- private Singleton2() { }
- public static Singleton2 GetSingleton()
- {
- lock (_lock)// 保证线程安全
- {
- if (_singleton == null)// 保证为空才创建
- {
- _singleton = new Singleton2();
- }
- return _singleton;
- }
- }
- }
话不多说, 看过代码的你多半你也会说一句, 加锁大法好. 到这我们基本上算是实现了, 但是我们看看这个锁, 虽然解决了多个实例对象问题, 但是该方式运行效率却很低, 下一个线程想要获取对象, 就必须等待上一个线程释放锁之后, 才可以继续运行, 但实际上对象大部分情况是已经存在了的并不需要 new, 因此对上个方法做了个优化.
懒汉优化模式(双检锁 / 双重校验锁(double-checked locking))
- /// <summary>
- /// 懒汉优化
- /// </summary>
- public class Singleton3
- {
- private static Singleton3 _singleton = null;
- private static object _lock = new object();
- private Singleton3() { }
- public static Singleton3 GetSingleton()
- {
- if (_singleton == null)// 保证对象初始化之后, 线程不需要等待锁
- {
- lock (_lock)// 保证线程安全
- {
- if (_singleton == null)// 保证为空才创建
- {
- _singleton = new Singleton3();
- }
- }
- }
- return _singleton;
- }
- }
到这我们想要的单例模式基本算 ok 了, 码得我自己都累, 想想设计模式这么多年的沉淀, 就没有简单点的写法么? 答案当然是肯定有的, 从实现方法的命名也可以看出, 除了懒汉模式, 还有对应的饿汉模式, 相对来说写法比较简单.
饿汉模式
- /// <summary>
- /// 饿汉模式 1
- /// </summary>
- public class Singleton4
- {
- private static Singleton4 _singleton = null;
- private Singleton4()
- {
- }
- /// <summary>
- /// 静态构造函数, 由 CLR 调用, 在使用之前被调用, 而且之调用一次
- /// </summary>
- static Singleton4()
- {
- _singleton = new Singleton4();
- }
- public static Singleton4 GetSingleton()
- {
- return _singleton;
- }
- }
- /// <summary>
- /// 饿汉模式 2
- /// </summary>
- public class Singleton5
- {
- /// <summary>
- /// 静态变量: 会在类型第一次使用的时候初始化, 而且只初始化一次
- /// </summary>
- private static Singleton5 _singleton = new Singleton5();
- private Singleton5()
- {
- }
- public static Singleton5 GetSingleton()
- {
- return _singleton;
- }
- }
以上俩种方式, 我个人认为是等价的, 一个应用了静态变量, 一个用的静态构造函数. 虽然写法相对简单, 不过, instance 在类装载时就实例化, 虽然导致类装载的原因有很多种, 在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式 (或者其他的静态方法) 导致类装载, 这时候初始化 instance 显然没有达到 lazy loading 的效果. 嗨, 有问题总得解决, 看看下面.
登记模式(静态内部类)
- public class Singleton6
- {
- private static class SingletonHolder
- {
- internal static Singleton6 _singleton = new Singleton6();
- }
- private Singleton6() { }
- public static Singleton6 GetSingleton()
- {
- return SingletonHolder._singleton;
- }
- }
这种方式同样利用了静态变量的机制来保证初始化 instance 时只有一个线程, 同时又解决了 lazy loading. 因为 SingletonHolder 类没有被主动使用, 只有通过显式调用 getInstance 方法时, 才会显式装载 SingletonHolder 类, 从而实例化 instance. 想象一下, 如果实例化 instance 很消耗资源, 所以想让它延迟加载, 另外一方面, 又不希望在 Singleton 类加载时就实例化, 这个时候, 这种方式相对来说比较实用.
综述
一般情况下, 懒汉优化模式更多的是让我们从头出发, 理解单例模式的运行过程和实现思路, 建议使用饿汉方式(Singleton4/Singleton5). 只有在要明确实现 lazy loading 效果时, 才会使用登记模式.
补充
事实上, 通过反射机制是能够实例化构造方法为 private 的类的, 那基本上会使所有的单例实现失效. 所以如果拿反射来讨论单例模式, 出门左转谢谢~
下载源码
来源: https://www.cnblogs.com/RenshuozZ/p/10616486.html