常见问题
客户程序需使用某个类(我们暂且把这个类叫做单例类),并且在同一时刻,客户程序中最多只能有这个单例类的一个实例。如何实现呢?
分析问题
要控制在同一时刻,客户程序中只有单例类的一个实例。在客户程序控制实例的唯一性是很难做到的,因为客户程序是多变的。在单例类本身实现实例唯一性控制比较可行,因为实例是由单例类的构造方法创建的,控制单例类的构造方法对客户程序隐藏,只有单例类自己可以创建实例,客户程序只能通过单例类对外公开的方法来获取单例类的实例。
即 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
解决方案
方案一:在这个单例类加载时创建这个类的实例并存入内存中,客户程序用到这个单例类实例时直接从内存中取。这样就保证内存中只有这个单例类的一个实例。也就是饿汉模式。
public class Singleton1 { private static Singleton1 _uniqueInstance = new Singleton1(); private Singleton1() { } public static Singleton1 Instance { get { return Singleton1._uniqueInstance; } } }
方案二:在客户程序需要用单例类的实例时,先在内存中查找是否已经有这个单例类的实例存在,如果已经有实例了,就直接使用这个实例;如果没有就创建这个单例类的一个新实例。也就是懒汉模式。只适用于单线程。
////// 单线程 /// public class Singleton2 { private static Singleton2 _uniqueInstance = null; private Singleton2() { } public static Singleton2 Instance { get { if (_uniqueInstance == null) { _uniqueInstance = new Singleton2(); } return _uniqueInstance; } } }
方案二不支持多线程情况,主要是第一次创建实例时,多线程和单线程有区别,其它情况处理相同。多线程第一次创建实例时,第一个线程执行完if (_uniqueInstance == null)后,此线程休眠,第二个线程开始执行,第二个线程也执行完if (_uniqueInstance == null),第一个线程被唤醒继续执行,这样第一个线程创建了一个单例类的实例;此后第一个线程休眠,第二个线程被唤醒,第二个线程也会创建一个单例类的实例。这样客户程序中就有单例类的两个实例。
方案三:改进方案二,使其支持多前程。用对象锁实现一锁双验证。////// 多线程 /// public class Singleton3 { //volatile标示的变量有关的运算,不要进行编译优化,以免出错 private static volatile Singleton3 _uniqueInstance = null; private static object lockHelper = new object(); private Singleton3() { } public static Singleton3 Instance { get { if (_uniqueInstance == null)//第一次创建实例对象 { lock (lockHelper)//防止多线程同时执行下面代码块 { if (_uniqueInstance == null)//二次验证防止多线程重复创建实例对象 { _uniqueInstance = new Singleton3(); } } } return _uniqueInstance; } } }
经典方案:借用.net静态构造方法机制简化方案三,实现多线程访问单例类。.net静态构造方法机制保证同一时刻只有一个线程在执行静态静态构造方法,类在访问静态属性前先访问静态静态构造方法来初始化静态属性。
////// 经典单例模式 /// public class Singleton1 { private static readonly Singleton1 _uniqueInstance = new Singleton1(); private Singleton1() { } }
扩展
怎么做才能让一个类不被客户程序实例化:
一、将这个类定义为抽象类。
二、将这个类的构造方法类型设置为:private类型,这样客户程序就不能通过new ClassName()方法来创建这个类的实例。
常用的创建对象实例的方法有三种:
一、使用new关键字访问类的构造方法。
二、使用反射动态创建类的实例(工厂方法模式使用较多)。
三、使用反序列化深复制已存在的类的实例来创建一个新实例(原型模式使用较多)。