什么是Singleton模式?
只能生成一個(gè)實(shí)例的類是實(shí)現(xiàn)了Singleton模式的類型昼窗。
如何實(shí)現(xiàn)Singleton模式
解法一(只適用于單線程)
由于只能生成一個(gè)實(shí)例岖常,因此必須將構(gòu)造函數(shù)設(shè)為私有函數(shù)以禁止他人創(chuàng)建實(shí)例锅锨,可以定義一個(gè)靜態(tài)的實(shí)例恋沃,在需要的時(shí)候創(chuàng)建實(shí)例必搞。實(shí)現(xiàn)如下:
public sealed class Singleton1{
private Singleton1(){
}
private static Singleton1 instance = null;
public static Singleton1 Instance(){
if(instance==null){
instance= new Singleton1();
return instance;
}
}
}
上述代碼在instance為空時(shí)才創(chuàng)建一個(gè)實(shí)例以避免重復(fù)創(chuàng)建,同時(shí)把構(gòu)造函數(shù)定義為私有函數(shù)囊咏,這樣能確保只創(chuàng)建一個(gè)實(shí)例恕洲。解法二(可多線程工作但是效率不高)
解法一的代碼在單線程時(shí)工作正常匆笤,但是在多線程的情況就有問題了,如果兩線程同時(shí)運(yùn)行炮捧,都判斷instance為空,那么兩個(gè)線程都會(huì)創(chuàng)建實(shí)例惦银,這時(shí)候就不滿足單例模式的要求了咆课。為確保單例,需要加上一個(gè)同步鎖书蚪。代碼如下:
public sealed class Singleton2{
private Singleton2(){
}
private static final object syncObj=new Object();
private static Singleton2 instance = null;
public static Singleton2 Instance(){
lock(syncObj){
if(instance==null){
instance= new Singleton1();
return instance;
}
}
}
}
由于在同一時(shí)刻只能有一個(gè)線程得到同步鎖迅栅,但第一個(gè)線程加上鎖時(shí),第二個(gè)線程只能等待读存。第一個(gè)線程發(fā)現(xiàn)沒有實(shí)例時(shí)進(jìn)行創(chuàng)建。線程執(zhí)行后釋放同步鎖敬察,第二個(gè)線程加上同步鎖,并運(yùn)行代碼莲祸,但是發(fā)現(xiàn)實(shí)例已經(jīng)創(chuàng)建好了椭迎,就不會(huì)創(chuàng)建實(shí)例,所以保證了單例模式畜号。解法三(加鎖前后兩次判斷實(shí)例是否存在)
解法二不是很好,畢竟獲得實(shí)例會(huì)加上一個(gè)同步鎖药蜻,但是加鎖十分耗時(shí),如非必要應(yīng)該避免语泽。
public sealed class Singleton3{
private Singleton3(){
}
private static final object syncObj=new Object();
private static Singleton3 instance = null;
public static Singleton3 Instance(){
if(instance==null){
lock(syncObj){
if(instance==null){
instance= new Singleton1();
return instance;
}
}
}
}
}
只有當(dāng)instance為null時(shí)才進(jìn)行加鎖操作,如果instance已經(jīng)創(chuàng)建處理廊驼,那么就不用進(jìn)行加鎖惋砂,時(shí)間效率提高了。解法四(利用靜態(tài)構(gòu)造函數(shù))
public sealed class Singleton4{
private Singleton4(){
}
private static Singleton4 instance = new Singleton4();
public static Singleton4 Instance(){
return instance;
}
}
運(yùn)行時(shí)能夠確保只調(diào)用一次靜態(tài)構(gòu)造函數(shù)西饵,可保證只初始化一次instance酝掩。解法五(按需創(chuàng)建實(shí)例)
public sealed class Singleton5{
Singleton5(){
}
public static Singleton5 Instance(){
return instance;
}
class Nested{
static Nested(){
}
internal static final Singleton5 instance=new Singleton5();
}
}
在內(nèi)部定義一個(gè)私有類型Nested期虾,Nested只在屬性Singleton5.Instance中被用到驯嘱,他人無法使用Nested類型。當(dāng)?shù)谝淮握{(diào)用Singleton5.Instance得到Singleton5的實(shí)例會(huì)自動(dòng)調(diào)用Nested的靜態(tài)構(gòu)造函數(shù)創(chuàng)建實(shí)例instance;不調(diào)用Singleton5.Instance不會(huì)自動(dòng)調(diào)用Nested的靜態(tài)構(gòu)造函數(shù)鞠评,不創(chuàng)建實(shí)例,實(shí)現(xiàn)按需創(chuàng)建聋涨。
Singleton模式優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn)
- 實(shí)例控制
單例模式會(huì)阻止其他對(duì)象實(shí)例化其自己的單例對(duì)象的副本锥忿,從而確保所有對(duì)象都訪問唯一實(shí)例。 - 靈活性
因?yàn)轭惪刂屏藢?shí)例化過程敬鬓,所以類可以靈活更改實(shí)例化過程。
- 實(shí)例控制
- 缺點(diǎn)
- 開銷
雖然數(shù)量很少础芍,但如果每次對(duì)象請(qǐng)求引用時(shí)都要檢查是否存在類的實(shí)例数尿,將仍然需要一些開銷∮冶模可以通過使用靜態(tài)初始化解決此問題歼捐。 - 可能的開發(fā)混淆
使用單例對(duì)象(尤其在類庫(kù)中定義的對(duì)象)時(shí)晨汹,開發(fā)人員必須記住自己不能使用new關(guān)鍵字實(shí)例化對(duì)象。因?yàn)榭赡軣o法訪問庫(kù)源代碼剥扣,因此應(yīng)用程序開發(fā)人員可能會(huì)意外發(fā)現(xiàn)自己無法直接實(shí)例化此類铝穷。 - 對(duì)象生存期
不能解決刪除單個(gè)對(duì)象的問題。在提供內(nèi)存管理的語言中(例如基于.NET Framework的語言)曙聂,只有單例類能夠?qū)е聦?shí)例被取消分配,因?yàn)樗瑢?duì)該實(shí)例的私有引用刽锤。在某些語言中(如 C++)朦佩,其他類可以刪除對(duì)象實(shí)例庐氮,但這樣會(huì)導(dǎo)致單例類中出現(xiàn)懸浮引用。
- 開銷