几种常用的单例模式详解
Visual Basic .NET和 Visual C# .NET都属于.NET平台下的语言,它们之间的本质区别较小,区别基本都是语法结构上的,既然两种语言都属于.NET平台,并且最终都是通过MSIL和CLR机制来运行,要实现它们之间的转换并不难。
首先看一下单例的模型
逻辑模型图:
物理模型图:
单例模式的五种写法。
1.简单实现
这种方式创建的对于多线程来讲可能是不安全的,原因是如果某一时刻实例未创建,这时有多个线程在这一时刻同时判断出实例不存在,那么这些线程就会执行后续代码并对实例进行创建,这样必然会导致创建多个实例。
C# Code
public sealed class Singleton { static Singleton instance=null; Singleton() { } public static Singleton Instance { get { if (instance==null) { instance = new Singleton(); } return instance; } } }
VB Code
Public NotInheritable Class Singleton Shared m_instance As Singleton = Nothing Private Sub New() End Sub Public Shared ReadOnly Property Instance() As Singleton Get If m_instance Is Nothing Then m_instance = New Singleton() End If Return m_instance End Get End Property End Class
2.安全的线程
这种方式可以避免简单实现中可能创建多个实例的问题,原因就是由于有Lock或者SyncLock的存在。它的含义就是将一个对象锁定,使访问它的线程在同一时刻只能是一个而不能是多个,其它线程要想访问被锁定的对象,那么必须等到被锁定的对象释放为止。
这种做法是会影响性能的,原因就是不管实例是否创建,每次调用创建方法的时候都需要加锁,而这是没有必要的。
在代码中被锁定的对象是事先创建的也就是先New的,原因就是保证加锁的时候,被锁定的对象已经创建,否则如果该对象未创建就加锁会出错的,这一点请读者用的时候一定注意。
C# Code
public sealed class Singleton { static Singleton instance=null; static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { lock (padlock) { if (instance==null) { instance = new Singleton(); } return instance; } } } }
VB Code
Public NotInheritable Class Singleton Shared m_instance As Singleton = Nothing Shared ReadOnly padlock As New Object() Private Sub New() End Sub Public Shared ReadOnly Property Instance() As Singleton Get SyncLock padlock If m_instance Is Nothing Then m_instance = New Singleton() End If Return m_instance End SyncLock End Get End Property End Class
3.双重锁定
这种方式的与安全线程的区别就是并不是每次都加锁,而是在加锁之前加了一个判断,即先判断是否创建了实例,如果没有创建实例被那么再加锁。
而为什么会有第二重的判断是否存在实例呢,这也涉及到多线程的问题,具体是因为,当实例没有被创建时,多个线程通过第一层判断,这时只有一个线程锁定对象,并且创建单例对象,注意此时其它的线程并没有退回到第一层的判断之外,而是依然驻留在被锁定对象的外围等待被锁定的对象释放,假如这时被锁定的对象释放了,那么如果没有第二层的判断实例是否存在,则会出现另一个线程创建的第二实例,同理如果其他的线程也通过了第一层的判断,那么第三,第四等多个实例也会被创建,所以存在双层判断是必须的。
C# Code
public sealed class Singleton { static Singleton instance=null; static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { if (instance==null) { lock (padlock) { if (instance==null) { instance = new Singleton(); } } } return instance; } } }
VB Code
Public NotInheritable Class Singleton Shared m_instance As Singleton = Nothing Shared ReadOnly padlock As New Object() Private Sub New() End Sub Public Shared ReadOnly Property Instance() As Singleton Get If m_instance Is Nothing Then SyncLock padlock If m_instance Is Nothing Then m_instance = New Singleton() End If End SyncLock End If Return m_instance End Get End Property End Class
4.静态初始化(首选方式)
注意:代码简单是这种创建方式的最大优点。这种方式的创建被称为饿汉式单例类,之所以这样称,是站在CPU占用来讲的,首先从它的创建来看,这种方式创建的单例类是在类被加载的时候就实例化,就像饿汉一样,一点时间都不浪费的疯狂占用CPU。而前面介绍的的单例类的创建被称为懒汉式单例类,这种方式创建的单例类的实例化是要在第一次引用的时候才创建,就像个懒汉一样,能不创建就不创建,能托就托。
通过代码可以明显看出这种方式的实例对象在开始定义的时候是用了New关键字实例化了的,而这点也是饿汉与懒汉的区别。
它的缺点也是因为饿汉对CPU的占用导致出现在引用之前,导致我们对它的实例化很难控制,并不能按照我们想的仅仅在需要的时候实例化。
C# Code
public sealed class Singleton { static readonly Singleton instance=new Singleton(); static Singleton() { } Singleton() { } public static Singleton Instance { get { return instance; } } }
VB Code
Public NotInheritable Class Singleton Shared ReadOnly m_instance As New Singleton() Shared Sub New() End Sub Private Sub New() End Sub Public Shared ReadOnly Property Instance() As Singleton Get Return m_instance End Get End Property End Class
5.延迟初始化(比较常用)
这点好处就是在代码简化的同时,将单例的实例化延迟到第一次引用,这样相对静态初始化来说,则明显要高明很多。
C# Code
public sealed class Singleton { Singleton() { } public static Singleton Instance { get { return Nested.instance; } } class Nested { static Nested() { } internal static readonly Singleton instance = new Singleton(); } }
VB Code
Public NotInheritable Class Singleton Private Sub New() End Sub Public Shared ReadOnly Property Instance() As Singleton Get Return Nested.instance End Get End Property Private Class Nested Shared Sub New() End Sub Friend Shared ReadOnly instance As New Singleton() End Class End Class