您好,登錄后才能下訂單哦!
前言:說(shuō)起單例模式,可能大家都熟悉,可以說(shuō)是設(shè)計(jì)模式中出現(xiàn)頻率最高的一個(gè),為了徹底弄清單例,在這里我將說(shuō)明何為單例,單例模式的演變,已經(jīng)和靜態(tài)類之間的區(qū)別等。
何為單例,就是在一個(gè)應(yīng)用程序中只能有一個(gè)實(shí)例,就是保證對(duì)象只能被new一次。
懶漢我覺(jué)得這個(gè)名字很形象,就是很懶,所以別的對(duì)象加載,它就不加載,你調(diào)用我的時(shí)候我在加載。比喻hibernate中也有懶模式。ok我們開(kāi)始吧
一天小明去面試,面試官說(shuō),你給我寫個(gè)單例模式,小明一想這實(shí)在太簡(jiǎn)單了不暇思索很快寫出來(lái)了如下的單例模式
1 public class Singleton { 2 private static Singleton singleton; 3 public static Singleton getSingleton() 4 { 5 if(singleton==null) 6 { 7 singleton=new Singleton(); 8 } 9 return singleton;10 }11 }
然后面試官一看說(shuō):你這在高并發(fā)的時(shí)候有可能會(huì)產(chǎn)生多個(gè)singleton實(shí)例,小明一想怎么會(huì)呢,面試官解釋說(shuō),如果有2個(gè)線程T1,T2同時(shí)執(zhí)行,當(dāng)T1執(zhí)行到第6行的時(shí)候,時(shí)間片段到,系統(tǒng)開(kāi)始讓T2執(zhí)行,執(zhí)行到第9行,然后T1又開(kāi)始執(zhí)行,因?yàn)門1已經(jīng)做過(guò)判斷此時(shí)并不知道singleton已經(jīng)被實(shí)例化,所以singleton此時(shí)再次被實(shí)例化,這樣你系統(tǒng)就有2個(gè)singleton對(duì)象,那還是單例嗎,小明恍然大悟,這個(gè)我能解決,馬上又寫出下面這個(gè)
1 public class Singleton { 2 private static Singleton singleton; 3 4 public synchronized static Singleton getSingleton() 5 { 6 if(singleton==null) 7 { 8 singleton=new Singleton(); 9 }10 return singleton;11 }
面試官一看,加上了線程同步,這個(gè)時(shí)候確實(shí)能保證線程安全問(wèn)題,但是又提出了疑問(wèn),如果現(xiàn)在singleton已經(jīng)被實(shí)例化了,如果10個(gè)線程同時(shí)訪問(wèn),每次都要等待那么勢(shì)必造成性能極大的消耗,你有沒(méi)有別的方案解決問(wèn)題,小明思考一分鐘又寫下了下面一段代碼
1 public class Singleton { 2 private static Singleton singleton; 3 4 public static Singleton getSingleton() 5 { 6 if(singleton==null) 7 { 8 synchronized (Singleton.class) { 9 if(singleton==null)10 {11 singleton=new Singleton();12 }13 }14 }15 return singleton;16 }
面試官一看,果真在上面一段代碼的基礎(chǔ)上提升了不少性能,減少了不必要的等待,但是仔細(xì)一看說(shuō)你這代碼有點(diǎn)問(wèn)題,并不能保證線程的安全,小明說(shuō)怎么說(shuō)呢,然后面試官解釋說(shuō):如果有T1,T2兩個(gè)線程,T1線程運(yùn)行第六行發(fā)現(xiàn)singleton==null,就進(jìn)入第8行,開(kāi)始對(duì)singleton進(jìn)行實(shí)例化,因?yàn)閷?shí)例化中分為三步,第一步為對(duì)象開(kāi)辟內(nèi)存空間,第二步為對(duì)象初始化,第三步是把這個(gè)內(nèi)存地址賦給singleton,但是因?yàn)閖ava的內(nèi)存模式允許無(wú)序?qū)懭耄@樣一來(lái)會(huì)導(dǎo)致第二步和第三步位置調(diào)換,那么這樣一來(lái)就壞了,如果先允許第一步和第三步了,但是此時(shí)并沒(méi)有對(duì)對(duì)象進(jìn)行初始化,恰恰在此時(shí)T2進(jìn)入了第6行,經(jīng)過(guò)判斷singleton不為null,那么就會(huì)返回一個(gè)沒(méi)有被初始化的對(duì)象。小明聽(tīng)了覺(jué)得對(duì)啊,他說(shuō)我把內(nèi)存模式改為不允許無(wú)序?qū)懭氩痪托辛藛?,于是就把代碼修改為
1 public class Singleton { 2 private volatile static Singleton singleton;//表示有序?qū)懭?nbsp;3 4 public static Singleton getSingleton() 5 { 6 if(singleton==null) 7 { 8 synchronized (Singleton.class) { 9 if(singleton==null)10 {11 singleton=new Singleton();12 }13 }14 }15 return singleton;16 }
注釋1:對(duì)象實(shí)例化三步我這里做一個(gè)比喻,某個(gè)公司給員工分配一間寢室(指的就是在堆中開(kāi)辟了空間)然后呢給房子進(jìn)行一些標(biāo)配比喻分個(gè)空調(diào)、洗衣機(jī)什么的(對(duì)象初始化),在然后呢把鑰匙給到員工手里(對(duì)象進(jìn)行賦值)。
面試官又問(wèn)你知道餓漢模式怎么寫的嗎,小明一聽(tīng):哦餓漢,不就是很著急自己馬上進(jìn)行實(shí)例化,生怕自己無(wú)法實(shí)例化嗎,這個(gè)簡(jiǎn)單馬上寫了一個(gè)餓漢
public class Singleton { private static Singleton singleton=new Singleton(); public static Singleton getSingleton() { return singleton; } }
面試官一看確實(shí)不錯(cuò)。
小明一看上面的模式,自己突發(fā)奇想,餓漢模式著急創(chuàng)建對(duì)象,在加載時(shí)候消耗性能,而懶漢模式又存在線程安全問(wèn)題(優(yōu)化后沒(méi)有了)能不能結(jié)合一下呢,突然告訴面試官我還有一個(gè)比較好的方式來(lái)實(shí)現(xiàn),然后他寫了下面代碼
1 public class Singleton { 2 private static class SingletonManager{3 private final static Singleton SINGLETON=new Singleton();4 }5 public final static Singleton getSingleton()6 {7 return SingletonManager.SINGLETON;8 }
面試官一看,不錯(cuò)不錯(cuò),既保證了懶加載,同時(shí)也保證了線程安全問(wèn)題。
面試官又問(wèn)小明,那么你知道使用場(chǎng)景嗎,小明想了想說(shuō),既然在應(yīng)用程序中只有一個(gè)單例,那么勢(shì)必是用于共享資源,比喻數(shù)據(jù)庫(kù)連接池,線程池等都可以用單例模式。
面試官繼續(xù)問(wèn):靜態(tài)類同樣也是產(chǎn)生一個(gè)對(duì)象,和單例具有高度相似你知道他們區(qū)別嗎,小明回答說(shuō)
1:面向?qū)ο笾杏腥筇匦岳^承,封裝和多態(tài),但是靜態(tài)類是不可以繼承的,所以從oo角度來(lái)說(shuō)靜態(tài)類并不符合面向?qū)ο?,他們的類是不可以被覆蓋,所以靈活性要比單例差的多
2:由于靜態(tài)類的特殊他在編譯器已經(jīng)進(jìn)行實(shí)例化了并不能提供懶加載模式
3:對(duì)于項(xiàng)目中如果進(jìn)行單元測(cè)試,由于方法不能覆蓋同樣為測(cè)試帶來(lái)了困難
4:由于靜態(tài)類在編譯器已經(jīng)都被實(shí)例化,所以要比單例性能要快,如果只需要執(zhí)行一些靜態(tài)方法這個(gè)時(shí)候可以采用靜態(tài)類
單例模式看似簡(jiǎn)單,其實(shí)用起來(lái)還要考慮到很多問(wèn)題,現(xiàn)在我把這個(gè)過(guò)程基本總結(jié)了,當(dāng)然可能還有不足之處,歡迎指正。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。