从最常见的、线程不安全的到延后创建的、线程安全的、再到简洁高效的版本。
所有的这些实现都有以下四个特征:
- 只有一个构造函数,而且是私有的,不带参数的。这是为了防止其他类对其实例化(这和模式本身有冲突)。同时也防止了子类化 : 如果一个单例能被子类化 一次,就能被子类化两次,而如果每个子类可以创建一个实例,这与模式本身又产生了冲突。如果你(遇到这样的情况):只有在运行期才能知道实际的类型,因此 你需要一个父类的单例,你可以使用工厂模式。
- 类是密封的。这并不是必须的,严格的说,即如上一点所说的原因,可以提高JIT(Just-In-Time , 运行时编译执行的技术)的效率。
- 一个静态变量用来保存单例的引用。
- 一个用以访问单例引用的公用静态方法。
注意:所有这些实现都用到一个公用静态属性Instance,作为访问实例的方法。当然都可以替换为方法,这对线程安全和性能都没有影响。
版本1-非线程安全

上面的代码是非线程安全的。两个线程可能同时判断“if (instance==null)”,发现为True,于是都创建了实例,这又违背了单例模式。注意:事实上在表达式反应前实例已经被创建了,内存模型并不保证新的实例的值被其他的线程看到,除非对应的内存屏障已经通过了。(CPU越过内存屏障后,将刷新自已对存储器的缓冲状态,这样其他线程才能同步自己 的copy)
版本2-简单的线程安全

这 个实现是线程安全的。线程首先对共用的对象进行锁定,然后判断实例是否在之前已经创建。这里要小心内存屏障问题(在锁定的时候确保所有的读操作发生在所获得之后,在解锁的时候确保所有的写操作发生在锁释放之前),确保只有一个线程创建了实例(因为在同一时刻只有一个线程可以在执行那段代码,到了第二个线 程进入的时候,第一个线程已经完成了实例的创建,这样表达式返回false)。
不幸的是,由于在每次访问的时候都要进行锁定,所以影响了性能。(这对于多线程并发的高性能要求的应用显得尤为重要)。
注意有些该版本的实现对typeof(Singleton)进行锁定,但我是对类的私有静态变量进行锁定。对那些可被其他类访问的对象进行锁定或对类型进行锁定会导致性能问题甚至引起死锁。这是一个地雷,任何地方都有可能发生,只有对本就为锁定而创建的对象进行锁定或是那些因为某些目的而被锁定的文档才是安全的。通常这些对象都是私有的。这样有助于写出线程安全的程序。
