<轉(zhuǎn)貼-To Me> 概述
Singleton模式?
五種實現(xiàn)
1
.簡單實現(xiàn)
?1
public
?
sealed
?
class
?Singleton
?2
{
?3
????
static
?Singleton?instance
=
null
;
?4
?5
????Singleton()
?6
????
{
?7
????}
?8
?9
????
public
?
static
?Singleton?Instance
10
????
{
11
????????
get
12
????????
{
13
????????????
if
?(instance
==
null
)
14
????????????
{
15
????????????????instance?
=
?
new
?Singleton();
16
????????????}
17
????????????
return
?instance;
18
????????}
19
????}
20
}
這種方式的實現(xiàn)對于線程來說并不是安全的,因為在多線程的環(huán)境下有可能得到Sigleton類的多個實例。如果同時有兩個線程去判斷(instance == null),并且得到的結(jié)果為真,這時兩個線程都會創(chuàng)建類Sigleton的實例,這樣就違背了Sigleton模式的原則。實際上在上述代碼中,有可能在計算出表達(dá)式的值之前,對象實例已經(jīng)被創(chuàng)建,但是內(nèi)存模型并不能保證對象實例在第二個線程創(chuàng)建之前被發(fā)現(xiàn)。
該實現(xiàn)方式主要有兩個優(yōu)點:
l????????
由于實例是在 Instance屬性方法內(nèi)部創(chuàng)建的,因此類可以使用附加功能(例如,對子類進(jìn)行實例化),即使它可能引入不想要的依賴性。
l????????
直到對象要求產(chǎn)生一個實例才執(zhí)行實例化;這種方法稱為“惰性實例化”。惰性實例化避免了在應(yīng)用程序啟動時實例化不必要的 singleton。
2
.安全的線程
?1
public
?
sealed
?
class
?Singleton
?2
{
?3
????
static
?Singleton?instance
=
null
;
?4
????
static
?
readonly
?
object
?padlock?
=
?
new
?
object
();
?5
?6
????Singleton()
?7
????
{
?8
????}
?9
10
????
public
?
static
?Singleton?Instance
11
????
{
12
????????
get
13
????????
{
14
????????????
lock
?(padlock)
15
????????????
{
16
????????????????
if
?(instance
==
null
)
17
????????????????
{
18
????????????????????instance?
=
?
new
?Singleton();
19
????????????????}
20
????????????????
return
?instance;
21
????????????}
22
????????}
23
????}
24
}
25
26
這種方式的實現(xiàn)對于線程來說是安全的。我們首先創(chuàng)建了一個進(jìn)程輔助對象,線程在進(jìn)入時先對輔助對象加鎖然后再檢測對象是否被創(chuàng)建,這樣可以確保只有一個實例被創(chuàng)建,因為在同一個時刻加了鎖的那部分程序只有一個線程可以進(jìn)入。這種情況下,對象實例由最先進(jìn)入的那個線程創(chuàng)建,后來的線程在進(jìn)入時(
instence == null
)為假,不會再去創(chuàng)建對象實例了。但是這種實現(xiàn)方式增加了額外的開銷,損失了性能。
3
.雙重鎖定
?1
public
?
sealed
?
class
?Singleton
?2
{
?3
????
static
?Singleton?instance
=
null
;
?4
????
static
?
readonly
?
object
?padlock?
=
?
new
?
object
();
?5
?6
????Singleton()
?7
????
{
?8
????}
?9
10
????
public
?
static
?Singleton?Instance
11
????
{
12
????????
get
13
????????
{
14
????????????
if
?(instance
==
null
)
15
????????????
{
16
????????????????
lock
?(padlock)
17
????????????????
{
18
????????????????????
if
?(instance
==
null
)
19
????????????????????
{
20
????????????????????????instance?
=
?
new
?Singleton();
21
????????????????????}
22
????????????????}
23
????????????}
24
????????????
return
?instance;
25
????????}
26
????}
27
}
28
這種實現(xiàn)方式對多線程來說是安全的,同時線程不是每次都加鎖,只有判斷對象實例沒有被創(chuàng)建時它才加鎖,有了我們上面第一部分的里面的分析,我們知道,加鎖后還得再進(jìn)行對象是否已被創(chuàng)建的判斷。它
解決了線程并發(fā)問題,同時避免在每個
Instance
屬性方法的調(diào)用中都出現(xiàn)獨占鎖定。它還允許您將實例化延遲到第一次訪問對象時發(fā)生。實際上,應(yīng)用程序很少需要這種類型的實現(xiàn)。大多數(shù)情況下我們會用靜態(tài)初始化。
這種方式仍然有很多缺點:無法實現(xiàn)延遲初始化。
4
.靜態(tài)初始化
?1
public
?
sealed
?
class
?Singleton
?2
{
?3
????
static
?
readonly
?Singleton?instance
=
new
?Singleton();
?4
?5
????
static
?Singleton()
?6
????
{
?7
????}
?8
?9
????Singleton()
10
????
{
11
????}
12
13
????
public
?
static
?Singleton?Instance
14
????
{
15
????????
get
16
????????
{
17
????????????
return
?instance;
18
????????}
19
????}
20
}
21
看到上面這段富有戲劇性的代碼,我們可能會產(chǎn)生懷疑,這還是
Sigleton
模式嗎?
在此實現(xiàn)中,將在第一次引用類的任何成員時創(chuàng)建實例。公共語言運行庫負(fù)責(zé)處理變量初始化。該類標(biāo)記為 sealed 以阻止發(fā)生派生,而派生可能會增加實例。此外,變量標(biāo)記為 readonly,這意味著只能在靜態(tài)初始化期間(此處顯示的示例)或在類構(gòu)造函數(shù)中分配變量。
該實現(xiàn)與前面的示例類似,不同之處在于它依賴公共語言運行庫來初始化變量。它仍然可以用來解決 Singleton模式試圖解決的兩個基本問題:全局訪問和實例化控制。公共靜態(tài)屬性為訪問實例提供了一個全局訪問點。此外,由于構(gòu)造函數(shù)是私有的,因此不能在類本身以外實例化 Singleton 類;因此,變量引用的是可以在系統(tǒng)中存在的唯一的實例。
由于 Singleton 實例被私有靜態(tài)成員變量引用,因此在類首次被對 Instance屬性的調(diào)用所引用之前,不會發(fā)生實例化。
這種方法唯一的潛在缺點是,您對實例化機(jī)制的控制權(quán)較少。在Design Patterns形式中,您能夠在實例化之前使用非默認(rèn)的構(gòu)造函數(shù)或執(zhí)行其他任務(wù)。由于在此解決方案中由 .NET Framework 負(fù)責(zé)執(zhí)行初始化,因此您沒有這些選項。在大多數(shù)情況下,靜態(tài)初始化是在 .NET 中實現(xiàn) Singleton的首選方法。
5
.延遲初始化
?1
public
?
sealed
?
class
?Singleton
?2
{
?3
????Singleton()
?4
????
{
?5
????}
?6
?7
????
public
?
static
?Singleton?Instance
?8
????
{
?9
????????
get
10
????????
{
11
????????????
return
?Nested.instance;
12
????????}
13
????}
14
????
15
????
class
?Nested
16
????
{
17
????????
static
?Nested()
18
????????
{
19
????????}
20
21
????????
internal
?
static
?
readonly
?Singleton?instance?
=
?
new
?Singleton();
22
????}
23
}
24
這里,初始化工作有Nested類的一個靜態(tài)成員來完成,這樣就實現(xiàn)了延遲初始化,并具有很多的優(yōu)勢,是值得推薦的一種實
現(xiàn)方式。
實現(xiàn)要點
l??????? Sigleton模式是限制而不是改進(jìn)類的創(chuàng)建。l???????? Sigleton類中的實例構(gòu)造器可以設(shè)置為Protected以允許子類派生。 l???????? Sigleton模式一般不要支持Icloneable接口,因為這可能導(dǎo)致多個對象實例,與Sigleton模式的初衷違背。 l???????? Sigleton模式一般不要支持序列化,這也有可能導(dǎo)致多個對象實例,這也與Sigleton模式的初衷違背。 l???????? Sigleton只考慮了對象創(chuàng)建的管理,沒有考慮到銷毀的管理,就支持垃圾回收的平臺和對象的開銷來講,我們一般沒必要對其銷毀進(jìn)行特殊的管理。 l???????? 理解和擴(kuò)展Sigleton模式的核心是“如何控制用戶使用new對一個類的構(gòu)造器的任意調(diào)用”。
l????????
可以很簡單的修改一個
Sigleton
,使它有少數(shù)幾個實例,這樣做是允許的而且是有意義的
。
優(yōu)點
l????????
實例控制:
Singleton
會阻止其他對象實例化其自己的 Singleton對象的副本,從而確保所有對象都訪問唯一實例
l????????
靈活性:因為類控制了實例化過程,所以類可以更加靈活修改實例化過程
缺點
l????????
開銷:雖然數(shù)量很少,
但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態(tài)初始化解決此問題,上面的五種實現(xiàn)方式中已經(jīng)說過了。
l?????????
可能的開發(fā)混淆:
使用
singleton
對象(尤其在類庫中定義的對象)時,開發(fā)人員必須記住自己不能使用
new
關(guān)鍵字實例化對象。因為可能無法訪問庫源代碼,因此應(yīng)用程序開發(fā)人員可能會意外發(fā)現(xiàn)自己無法直接實例化此類。
l????????
對象的生存期:
Singleton
不能解決刪除單個對象的問題。在提供內(nèi)存管理的語言中(例如基于 .NET Framework 的語言),只有 Singleton類能夠?qū)е聦嵗蝗∠峙洌驗樗瑢υ搶嵗乃接幸谩T谀承┱Z言中(如 C++),其他類可以刪除 對象實例,但這樣會導(dǎo)致 Singleton類中出現(xiàn)懸浮引用。
適用性
l????????
當(dāng)類只能有一個實例而且客戶可以從一個眾所周知的訪問點訪問它時。
l????????
當(dāng)這個唯一實例應(yīng)該是通過子類化可擴(kuò)展的,并且客戶應(yīng)該無需更改代碼就能使用一個擴(kuò)展的實例時。
應(yīng)用場景
l????????
每臺計算機(jī)可以有若干個打印機(jī),但只能有一個
Printer Spooler
,避免兩個打印作業(yè)同時輸出到打印機(jī)。 (摘自呂震宇的
C#
設(shè)計模式(7
)-Singleton Pattern
)
l????????
PC
機(jī)中可能有幾個串口,但只能有一個
COM1
口的實例。
l????????
系統(tǒng)中只能有一個窗口管理器。
l????????
.NET Remoting
中服務(wù)器激活對象中的
Sigleton
對象,確保所有的客戶程序的請求都只有一個實例來處理。
完整示例
這是一個簡單的計數(shù)器例子,四個線程同時進(jìn)行計數(shù)。
?1
using
?System;
?2
using
?System.Threading;
?3
?4
namespace
?SigletonPattern.SigletonCounter
?5
{
?6
????
/**/
///
?
<summary>
?7
????
///
?功能:簡單計數(shù)器的單件模式
?8
????
///
?編寫:Terrylee
?9
????
///
?日期:2005年12月06日
10
????
///
?
</summary>
11
????
public
?
class
?CountSigleton
12
????
{
13
????????
/**/
///
存儲唯一的實例
14
????????
static
?CountSigleton?uniCounter?
=
?
new
?CountSigleton();??
15
???
16
????????
/**/
///
存儲計數(shù)值
17
????????
private
?
int
?totNum?
=
?
0
;??
18
???
19
????????
private
?CountSigleton()?
20
???
21
????????
{?
22
????????????
/**/
///
線程延遲2000毫秒
23
????????????Thread.Sleep(
2000
);
24
????????}
?
25
???
26
????????
static
?
public
?CountSigleton?Instance()?
27
???
28
????????
{?
29
???
30
????????????
return
?uniCounter;?
31
???
32
????????}
?
33
????????
34
????????
/**/
///
計數(shù)加1
35
????????
public
?
void
?Add()
36
????????
{?
37
????????????totNum?
++
;
38
????????}
??
39
????????
40
????????
/**/
///
獲得當(dāng)前計數(shù)值
41
????????
public
?
int
?GetCounter()
42
????????
{?
43
????????????
return
?totNum;
44
????????}
?
45
46
????}
47
}
48
?1
using
?System;
?2
using
?System.Threading;
?3
using
?System.Text;
?4
?5
namespace
?SigletonPattern.SigletonCounter
?6
{
?7
????
/**/
///
?
<summary>
?8
????
///
?功能:創(chuàng)建一個多線程計數(shù)的類
?9
????
///
?編寫:Terrylee
10
????
///
?日期:2005年12月06日
11
????
///
?
</summary>
12
????
public
?
class
?CountMutilThread
13
????
{
14
????????
public
?CountMutilThread()
15
????????
{
16
????????????
17
????????}
18
19
????????
/**/
///
?
<summary>
20
????????
///
?線程工作
21
????????
///
?
</summary>
22
????????
public
?
static
?
void
?DoSomeWork()
23
????????
{
24
????????????
/**/
///
構(gòu)造顯示字符串
25
????????????
string
?results?
=
?
""
;
26
27
????????????
/**/
///
創(chuàng)建一個Sigleton實例
28
???CountSigleton?MyCounter?
=
?CountSigleton.Instance();
29
30
????????????
/**/
///
循環(huán)調(diào)用四次
31
????????????
for
(
int
?i
=
1
;i
<
5
;i
++
)
32
????????????
{
33
???????
/**/
///
開始計數(shù)
34
????????????????MyCounter.Add();
35
????????????????
36
????????????????results?
+=
"
線程
"
;
37
????????????????results?
+=
?i.ToString()?
+
?
"
——〉
"
;
38
????????????????results?
+=
?
"
當(dāng)前的計數(shù):
"
;
39
????????????????results?
+=
?MyCounter.GetCounter().ToString();
40
????????????????results?
+=
?
"
\n
"
;
41
42
????????????????Console.WriteLine(results);
43
????????????????
44
????????????????
/**/
///
清空顯示字符串
45
?????????results?
=
?
""
;
46
????????????}
47
????????}
48
49
????????
public
?
void
?StartMain()
50
????????
{
51
52
????????????Thread?thread0?
=
?Thread.CurrentThread;?
53
???
54
????????????thread0.Name?
=
?
"
Thread?0
"
;?
55
???
56
????????????Thread?thread1?
=
new
?Thread(
new
?ThreadStart(DoSomeWork));?
57
???
58
????????????thread1.Name?
=
?
"
Thread?1
"
;?
59
???
60
????????????Thread?thread2?
=
new
?Thread(
new
?ThreadStart(DoSomeWork));?
61
???
62
????????????thread2.Name?
=
?
"
Thread?2
"
;?
63
???
64
????????????Thread?thread3?
=
new
?Thread(
new
?ThreadStart(DoSomeWork));?
65
???
66
????????????thread3.Name?
=
?
"
Thread?3
"
;?
67
???
68
????????????thread1.Start();?
69
???
70
????????????thread2.Start();?
71
???
72
????????????thread3.Start();?
73
????????????
74
????????????
/**/
///
線程0也只執(zhí)行和其他線程相同的工作
75
????????????DoSomeWork();?
76
????????}
77
????}
78
}
79
?1
using
?System;
?2
using
?System.Text;
?3
using
?System.Threading;
?4
?5
namespace
?SigletonPattern.SigletonCounter
?6
{
?7
????
/**/
///
?
<summary>
?8
????
///
?功能:實現(xiàn)多線程計數(shù)器的客戶端
?9
????
///
?編寫:Terrylee
10
????
///
?日期:2005年12月06日
11
????
///
?
</summary>
12
????
public
?
class
?CountClient
13
????
{
14
????????
public
?
static
?
void
?Main(
string
[]?args)
15
????????
{
16
???????CountMutilThread?cmt?
=
?
new
?CountMutilThread();
17
18
????????????cmt.StartMain();
19
20
????????????Console.ReadLine();
21
????????}
22
????}
23
}
24
總結(jié)
Sigleton
設(shè)計模式是一個非常有用的機(jī)制,可用于在面向?qū)ο蟮膽?yīng)用程序中提供單個訪問點。文中通過五種實現(xiàn)方式的比較和一個完整的示例,完成了對
Sigleton
模式的一個總結(jié)和探索。用一句廣告詞來概括
Sigleton
模式就是“簡約而不簡單”。
源碼下載:/Files/Terrylee/SigletonPattern.rar
|