关于DCL模式的讨论
第一次接触到这个技巧的读者必定会有很多问题,诸如第一次检查或者第二次检查可不可以省掉等等。回答是,按照多线程的原理和DCL模式的预想方案,它们是不可以省掉的。
首先,如果省略了第一次检查,那么工厂方法就变成下面这样:
Public Shared Function GetInstance() As Product
Thread.Sleep(10)
'位置1
'位置2
m.WaitOne()
'位置3
If (instance Is Nothing) Then '位置4
instance = New Product()
End If
m.ReleaseMutex()
Return instance
End Function
代码清单7、省略了第一重检查的线程安全的工厂方法 Thread.Sleep(10)
'位置1
'位置2
m.WaitOne()
'位置3
If (instance Is Nothing) Then '位置4
instance = New Product()
End If
m.ReleaseMutex()
Return instance
End Function
这就造成不论产品实例是否存在都会在位置2等待的情况,也就是等于没有优化前的线程安全的工厂方法(参见代码清单5),虽然并没有错误地产生多于一个的产品对象,但也没有达到优化的目的。
其次,如果省略了第二重检查的话,工厂方法模式就会变成下面这样:
If (instance Is Nothing) Then '位置1
'位置2
m.WaitOne()
'位置3
instance = New Product()
m.ReleaseMutex()
End If
Return instance
代码清单8、省略了第二重检查的线程安全的工厂方法 '位置2
m.WaitOne()
'位置3
instance = New Product()
m.ReleaseMutex()
End If
Return instance
这是否可以呢?同样假设线程A和B作为第一批调用者同时或几乎同时调用静态工厂方法。
1. 因为线程A和B是第一批调用者,因此当它们进入此静态工厂方法时,instance变量是Nothing。因此线程A和B会同时或几乎同时到达位置1。
2. 假设线程A会首先到达位置2,并进入m.WaitOne()而到达位置3。这时,由于m.WaitOne()的同步化限制,线程B无法到达位置3,而只能在位置2等候。
3. 线程A执行instance = New Product()语句,使得instance变量得到一个值,即对一个Product对象的引用。此时,线程B只能继续在位置2等候。
4. 线程A退出m.WaitOne(),返回instance对象,退出静态工厂方法。
5. 线程B进入m.WaitOne()块,达到位置3,线程B执行instance = New Product()语句,使得instance变量得到一个新值,B退出静态工厂方法。
因此线程A和B创建了两个产品类的实例。换言之,没有第二重检查是不可以的。
DCL模式在Singleton模式中的应用
Singleton模式描述的是只有一个实例的类,这个类叫做Singleton类。Singleton类自己向外界提供自己的唯一实例。一般情况下,Singleton模式多使用在多线程环境中,这使得线程同步化变得非常重要。
根据Singleton类的实例创建方式的不同,Singleton模式的实现可以分成两种:"饿汉式"和"懒汉式"。"懒汉式"Singleton模式会在工厂方法被调用的时候判断是否需要创建产品的实例:如果实例已经存在了,就直接返还这个实例,反之就首先创建一个实例,再存储起来,然后返还这个实例。
熟悉Singleton模式的读者应该可以注意到,DCL模式可以使用到"懒汉式"的Singleton模式中。实际上,Singleton类就是DCL模式的特殊情况,只要把工厂类与产品类合并就可以得到Singleton类。请参见下面的UML类图。

下面就是这个Singleton类的源代码:
Public Class Singleton
Private Shared instance As Singleton
Private Shared m As Mutex = New Mutex()
Public Sub New()
System.Console.WriteLine("Singleton object is created.")
End Sub
Public Shared Function GetInstance() As Singleton
Thread.Sleep(10)
If instance Is Nothing Then
m.WaitOne()
If instance Is Nothing Then
instance = New Singleton()
End If
m.ReleaseMutex()
End If
Return instance
End Function
End Class
代码清单9、二重检查的线程安全的Singleton类
Private Shared instance As Singleton
Private Shared m As Mutex = New Mutex()
Public Sub New()
System.Console.WriteLine("Singleton object is created.")
End Sub
Public Shared Function GetInstance() As Singleton
Thread.Sleep(10)
If instance Is Nothing Then
m.WaitOne()
If instance Is Nothing Then
instance = New Singleton()
End If
m.ReleaseMutex()
End If
Return instance
End Function
End Class
