1.什么是單例模式
- 生成一個(gè)獨(dú)一無(wú)二的,保證任何時(shí)刻一個(gè)類(lèi)只有一個(gè)實(shí)例的模式
- 確保一個(gè)類(lèi)只有一個(gè)實(shí)例,并提供一個(gè)全局訪(fǎng)問(wèn)點(diǎn)
- 可以在需要時(shí)才創(chuàng)建對(duì)象,避免了全局變量在程序啟動(dòng)時(shí)就得創(chuàng)建對(duì)象的缺點(diǎn)。
2.經(jīng)典單例模式實(shí)現(xiàn)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class myinstance{ //第一步:私有化構(gòu)造器,只有類(lèi)自身才能調(diào)用構(gòu)造器外部類(lèi)不能夠直接new出這個(gè)類(lèi)的實(shí)例對(duì)象 private myinstance(){} //第二步:聲明一個(gè)全局靜態(tài)變量來(lái)記錄自身實(shí)例的對(duì)象,也是私有的,限制其它外部類(lèi)訪(fǎng)問(wèn) private static myinstance myinstance; //第三步:提供一個(gè)外部可訪(fǎng)問(wèn)的靜態(tài)公開(kāi)方法,來(lái)獲得該類(lèi)的唯一實(shí)例 public static myinstance getmyinstance(){ //第四步:進(jìn)行判斷自身類(lèi)對(duì)象如果為空,則創(chuàng)建一個(gè)實(shí)例 if (myinstance== null ){ //這里的方法只執(zhí)行了一次,生成了一個(gè)唯一的類(lèi)對(duì)象 myinstance= new myinstance(); } //第五步:如果不為空,則返回該類(lèi)對(duì)象,故由始至終,該類(lèi)對(duì)象只初始化過(guò)一次,只有一個(gè)對(duì)象存在,這就是單例模式 return myinstance; } } |
3.經(jīng)典模式存在的缺陷
這種經(jīng)典模式也稱(chēng)之為懶漢式單例模式(lazy instantiaze),因?yàn)樗茄舆t化實(shí)例化的,即如果我們不需要這個(gè)實(shí)例,則它永遠(yuǎn)不會(huì)被初始化,只有在調(diào)用過(guò)一次實(shí)例化方法后,才會(huì)被創(chuàng)建出對(duì)象。
在多線(xiàn)程的情況下可能會(huì)產(chǎn)生并發(fā)問(wèn)題,因?yàn)楂@取單例的方法getmyinstance()
有可能被多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn),這時(shí)就會(huì)有可能 創(chuàng)建出兩個(gè)以上的實(shí)例對(duì)象。這就要考慮要線(xiàn)程安全的問(wèn)題了,解決問(wèn)題就是在獲取實(shí)例方法處加一個(gè)同步鎖,這樣就能輕松地解決線(xiàn)程并發(fā)的問(wèn)題了。
1
2
3
4
5
6
|
public static synchronized myinstance getmyinstance(){ //加同步鎖關(guān)鍵字synchronized,這樣在有線(xiàn)程訪(fǎng)問(wèn)這個(gè)方法時(shí),其它線(xiàn)程只能等待當(dāng)前線(xiàn)程訪(fǎng)問(wèn)結(jié)束才能訪(fǎng)問(wèn)這個(gè)方法。 if (myinstance== null ){ //這里的方法只執(zhí)行了一次,生成了一個(gè)唯一的類(lèi)對(duì)象 myinstance= new myinstance(); } |
4.多線(xiàn)程下同步所造成的性能問(wèn)題
如果將獲取實(shí)例的方法進(jìn)行同步的話(huà),會(huì)造成程序執(zhí)行的效率大大地下降,而且單例對(duì)象生成只要調(diào)用一次方法即可,之后每次調(diào)用這個(gè)方法時(shí),同步都是一種累綴,有可能會(huì)拖垮程序的性能。
當(dāng)然如果你的程序?qū)τ谛阅艿囊蟛⒉皇呛芨叩脑?huà),用同步的方法獲取單例是最簡(jiǎn)單而有效的。
為保證程序的性能并且又不會(huì)出現(xiàn)并發(fā)的問(wèn)題,可以使用另一種生成單例對(duì)象的模式,叫做餓漢式單例模式(eagerly instantiaze)
1
2
3
4
5
6
7
8
9
|
public class myinstance{ //第一步:私有化構(gòu)造器 private myinstance(){} //第二步:聲明一個(gè)全局靜態(tài)變量來(lái)記錄自身實(shí)例的對(duì)象,并進(jìn)行實(shí)例化 private static myinstance myinstance= new myinstance(); //第三步:提供一個(gè)外部可訪(fǎng)問(wèn)的靜態(tài)公開(kāi)方法,來(lái)獲得該類(lèi)的唯一實(shí)例 public static myinstance getmyinstance(){ return myinstance; } |
這個(gè)模式使jvm在加載這個(gè)類(lèi)時(shí)會(huì)馬上創(chuàng)建唯一的單例對(duì)象,這樣就能保證任何線(xiàn)程訪(fǎng)問(wèn)靜態(tài)單例變量myinstance時(shí),單例對(duì)象一定被實(shí)例化過(guò)了。
5.利用雙重檢查加鎖來(lái)提升性能
- 首先檢查實(shí)例是否已經(jīng)創(chuàng)建了,如果沒(méi)有才進(jìn)行同步獲取實(shí)例的方法,這樣就保證了實(shí)例方法只會(huì)在第一次獲取實(shí)例時(shí)會(huì)同步。
- 這里要用到一個(gè)關(guān)鍵字volatile,此關(guān)鍵字確保了當(dāng)實(shí)例變量myinstance被初始化成實(shí)例對(duì)象時(shí),多個(gè)線(xiàn)程能正確地處理實(shí)例變量。注意,這個(gè)關(guān)鍵字只有在java1.5及以上的版本才會(huì)對(duì)雙重檢查加載生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class myinstance{ //用關(guān)鍵字volatile修飾實(shí)例變量 private volatile static myinstance myinstance; //私有化構(gòu)造器 private myinstance(){} public static myinstance getmyinstance(){ //第一次檢查實(shí)例是否存在 if (myinstance== null ){ //如果不存在則進(jìn)入同步區(qū)塊 synchronized (myinstance. class ){ if (myinstance== null ){ //第二次檢查,如果不為空才真正創(chuàng)建實(shí)例對(duì)象 myinstance= new myinstance(); } } } //如果不為空,則直接返回該類(lèi)對(duì)象 return myinstance; } } |
單例模式的所有情況都已經(jīng)總結(jié)完畢,一開(kāi)始以為單例模式應(yīng)該是所有設(shè)計(jì)模式中最簡(jiǎn)單易懂的了,沒(méi)想到看到四人幫的headfirst設(shè)計(jì)模式后發(fā)現(xiàn)還有這么多門(mén)道,真的是學(xué)無(wú)止境。
注:以上所有內(nèi)容皆總結(jié)自《headfirst 設(shè)計(jì)模式》
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)服務(wù)器之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接
原文鏈接:https://blog.csdn.net/pigdreams/article/details/53232731