
1. 概念
所谓的单例模式是为了确保一个类只有一个实例singleton单例模式,并提供一个全局访问点.
2. UML图

3. 应用场景
举个小例子,在Windows桌面上,我们打开了一个回收站. 当我们尝试再次打开新的回收站时,Windows系统将不会为您弹出新的回收站窗口. 也就是说,在整个系统运行期间,系统仅维护一个回收站实例. 这是典型的单例模型.
因此,单例模式的应用场景为:

①需要经常创建和销毁的对象;
②需要花费大量时间或资源来创建但经常使用的对象;
③工具对象;
④经常访问或文件的对象.
4. 实施思路
从单例模式的概念开始(确保一个类只有一个实例,并提供一个访问它的全局访问点),该概念可以分为两部分: (1)确保只有一个实例类的实例; 2)提供一个全局访问点进行访问.
问: 如何确保一个类只有一个实例?

答案: 定义私有构造函数无法通过外界的new创建实例(通常,当我们创建新类时,如果不编写构造函数,则编译器默认会生成一个公共的无参数构造函数. 您可以使用new来创建该类的实例).
问: 那您要在哪里创建类的实例?
答案: 在类别中创建. 要创建类的实例,您需要一个私有变量来保存该实例,并且为了确保在多线程情况下只有一个实例,它必须是静态变量.
问: 外界如何获得此类的实例以使用它?
答案: 定义一个公共方法以提供一个全局访问点,并定义公共属性以提供一个全局访问点.
5. 实施代码
// 单例模式的实现
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance;
// 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
}
// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
public static Singleton GetInstance()
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}

上面的单例模式实现确实在单线程下是完美的,但是,在多线程的情况下,您将获得多个单例实例,因为当两个线程同时运行GetInstance方法时,两个线程将判断(uniqueInstance == null)此条件返回true. 这时,两个线程都将创建一个Singleton实例,这违反了我们对Singleton模式的初衷. 由于上述实现将运行多个线程来执行,因此我们的多线程解决方案自然是使GetInstance方法一次仅运行一个线程,即需要线程同步.
//单例模式的实现
public class Singleton
{
// 定义一个静态变量来保存类的实例
private static Singleton uniqueInstance;
// 定义一个标识确保线程同步
private static readonly object locker = new object();
// 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
}
//定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
// 双重锁定只需要一句判断就可以了
if (uniqueInstance == null)
{
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
问题: 为什么需要在多线程GetInstance中进行两个(uniqueInstance == null)判断?最好只判断一个?
说明: 当uniqueInstance不为null时,您仅需要直接返回uniqueInstance对象,而无需锁定辅助对象. 锁定会增加额外的开销,并且会降低性能singleton单例模式,因此我们只需要在判断可以避免锁定的额外开销之前添加一个语句(uniqueInstance == null)即可. 此实现称为“双重锁定”,这是第一种(uniqueInstance == null)的必要性. 在多线程中,如果两个线程同时运行锁的外层的if(uniqueInstance == null),则会同时建立两个线程. 第一个线程锁定并实例化一个对象. 解锁后,如果没有判断,第二个线程将直接实例化一个对象,这不是单例. 因此,第二个(uniqueInstance == null)判断也是必要的.
6. 主要优点
①提供对唯一实例的受控访问.
②由于系统内存中仅存在一个对象,因此可以节省系统资源,某些需要频繁创建和销毁的对象的单例模式无疑可以提高系统性能.

③允许可变数量的实例.
7. 主要缺点
①由于简单兴趣模型中没有抽象层,因此很难扩展单例类.
②单例类别的职责过于沉重,在一定程度上违反了“单一职责原则”.
③单例滥用会带来一些负面问题. 例如,为了节省资源,连接池对象被设计为一个单例类,这可能导致共享该连接池对象的程序过多,连接池将溢出. 该对象长时间不使用,系统将被视为垃圾并被回收,这将导致对象状态的丢失.
返回(23种设计模式)
参考:
1,C#设计模式(1)-单例模式
2. 单例模式的使用和应用场景
3. 单例模式的八种书写方式
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-166525-1.html
10月16日一定去看
知道为什么中共对所谓的岛内运动不屑一顾吗