




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
...wd......wd......wd...深入Java單例模式2009-09-2010:57:12標(biāo)簽:Java原創(chuàng)作品,允許轉(zhuǎn)載,轉(zhuǎn)載時(shí)請(qǐng)務(wù)必以超鏈接形式標(biāo)明文章原始出處、作者信息和本聲明。否則將追究法律責(zé)任。://devbean.blog.51cto/448512/203501在GoF的23種設(shè)計(jì)模式中,單例模式是比較簡(jiǎn)單的一種。然而,有時(shí)候越是簡(jiǎn)單的東西越容易出現(xiàn)問(wèn)題。下面就單例設(shè)計(jì)模式詳細(xì)的探討一下。所謂單例模式,簡(jiǎn)單來(lái)說(shuō),就是在整個(gè)應(yīng)用中保證只有一個(gè)類的實(shí)例存在。就像是JavaWeb中的application,也就是提供了一個(gè)全局變量,用處相當(dāng)廣泛,比方保存全局?jǐn)?shù)據(jù),實(shí)現(xiàn)全局性的操作等。1.最簡(jiǎn)單的實(shí)現(xiàn)首先,能夠想到的最簡(jiǎn)單的實(shí)現(xiàn)是,把類的構(gòu)造函數(shù)寫成private的,從而保證別的類不能實(shí)例化此類,然后在類中提供一個(gè)靜態(tài)的實(shí)例并能夠返回給使用者。這樣,使用者就可以通過(guò)這個(gè)引用使用到這個(gè)類的實(shí)例了。publicclassSingletonClass{
privatestaticfinalSingletonClassinstance=newSingletonClass();
publicstaticSingletonClassgetInstance(){
returninstance;
}
privateSingletonClass(){
}
}如上例,外部使用者如果需要使用SingletonClass的實(shí)例,只能通過(guò)getInstance()方法,并且它的構(gòu)造方法是private的,這樣就保證了只能有一個(gè)對(duì)象存在。2.性能優(yōu)化——lazyloaded上面的代碼雖然簡(jiǎn)單,但是有一個(gè)問(wèn)題——無(wú)論這個(gè)類是否被使用,都會(huì)創(chuàng)立一個(gè)instance對(duì)象。如果這個(gè)創(chuàng)立過(guò)程很耗時(shí),比方需要連接10000次數(shù)據(jù)庫(kù)(夸張了…:-)),并且這個(gè)類還并不一定會(huì)被使用,那么這個(gè)創(chuàng)立過(guò)程就是無(wú)用的。怎么辦呢為了解決這個(gè)問(wèn)題,我們想到了新的解決方案:publicclassSingletonClass{
privatestaticSingletonClassinstance=null;
publicstaticSingletonClassgetInstance(){
if(instance==null){
instance=newSingletonClass();
}
returninstance;
}
privateSingletonClass(){
}
}代碼的變化有兩處——首先,把instance初始化為null,直到第一次使用的時(shí)候通過(guò)判斷是否為null來(lái)創(chuàng)立對(duì)象。因?yàn)閯?chuàng)立過(guò)程不在聲明處,所以那個(gè)final的修飾必須去掉。我們來(lái)想象一下這個(gè)過(guò)程。要使用SingletonClass,調(diào)用getInstance()方法。第一次的時(shí)候發(fā)現(xiàn)instance是null,然后就新建一個(gè)對(duì)象,返回出去;第二次再使用的時(shí)候,因?yàn)檫@個(gè)instance是static的,所以已經(jīng)不是null了,因此不會(huì)再創(chuàng)立對(duì)象,直接將其返回。這個(gè)過(guò)程就成為lazyloaded,也就是遲加載——直到使用的時(shí)候才進(jìn)展加載。3.同步上面的代碼很清楚,也很簡(jiǎn)單。然而就像那句名言:“80%的錯(cuò)誤都是由20%代碼優(yōu)化引起的〞。單線程下,這段代碼沒有什么問(wèn)題,可是如果是多線程,麻煩就來(lái)了。我們來(lái)分析一下:線程A希望使用SingletonClass,調(diào)用getInstance()方法。因?yàn)槭堑谝淮握{(diào)用,A就發(fā)現(xiàn)instance是null的,于是它開場(chǎng)創(chuàng)立實(shí)例,就在這個(gè)時(shí)候,CPU發(fā)生時(shí)間片切換,線程B開場(chǎng)執(zhí)行,它要使用SingletonClass,調(diào)用getInstance()方法,同樣檢測(cè)到instance是null——注意,這是在A檢測(cè)完之后切換的,也就是說(shuō)A并沒有來(lái)得及創(chuàng)立對(duì)象——因此B開場(chǎng)創(chuàng)立。B創(chuàng)立完成后,切換到A繼續(xù)執(zhí)行,因?yàn)樗呀?jīng)檢測(cè)完了,所以A不會(huì)再檢測(cè)一遍,它會(huì)直接創(chuàng)立對(duì)象。這樣,線程A和B各自擁有一個(gè)SingletonClass的對(duì)象——單例失敗!解決的方法也很簡(jiǎn)單,那就是加鎖:publicclassSingletonClass{
privatestaticSingletonClassinstance=null;
publicsynchronizedstaticSingletonClassgetInstance(){
if(instance==null){
instance=newSingletonClass();
}
returninstance;
}
privateSingletonClass(){
}
}是要getInstance()加上同步鎖,一個(gè)線程必須等待另外一個(gè)線程創(chuàng)立完成后才能使用這個(gè)方法,這就保證了單例的唯一性。4.又是性能上面的代碼又是很清楚很簡(jiǎn)單的,然而,簡(jiǎn)單的東西往往不夠理想。這段代碼毫無(wú)疑問(wèn)存在性能的問(wèn)題——synchronized修飾的同步塊可是要比一般的代碼段慢上幾倍的!如果存在很屢次getInstance()的調(diào)用,那性能問(wèn)題就不得不考慮了!讓我們來(lái)分析一下,終究是整個(gè)方法都必須加鎖,還是僅僅其中某一句加鎖就足夠了我們?yōu)槭裁匆渔i呢分析一下出現(xiàn)lazyloaded的那種情形的原因。原因就是檢測(cè)null的操作和創(chuàng)立對(duì)象的操作別離了。如果這兩個(gè)操作能夠原子地進(jìn)展,那么單例就已經(jīng)保證了。于是,我們開場(chǎng)修改代碼:publicclassSingletonClass{
privatestaticSingletonClassinstance=null;
publicstaticSingletonClassgetInstance(){
synchronized(SingletonClass.class){
if(instance==null){
instance=newSingletonClass();
}
}
returninstance;
}
privateSingletonClass(){
}
}首先去掉getInstance()的同步操作,然后把同步鎖加載if語(yǔ)句上。但是這樣的修改起不到任何作用:因?yàn)槊看握{(diào)用getInstance()的時(shí)候必然要同步,性能問(wèn)題還是存在。如果……如果我們事先判斷一下是不是為null再去同步呢publicclassSingletonClass{
privatestaticSingletonClassinstance=null;
publicstaticSingletonClassgetInstance(){
if(instance==null){
synchronized(SingletonClass.class){
if(instance==null){
instance=newSingletonClass();
}
}
}
returninstance;
}
privateSingletonClass(){
}
}還有問(wèn)題嗎首先判斷instance是不是為null,如果為null,加鎖初始化;如果不為null,直接返回instance。這就是double-checkedlocking設(shè)計(jì)實(shí)現(xiàn)單例模式。到此為止,一切都很完美。我們用一種很聰明的方式實(shí)現(xiàn)了單例模式。5.從源頭檢查下面我們開場(chǎng)說(shuō)編譯原理。所謂編譯,就是把源代碼“翻譯〞成目標(biāo)代碼——大多數(shù)是指機(jī)器代碼——的過(guò)程。針對(duì)Java,它的目標(biāo)代碼不是本地機(jī)器代碼,而是虛擬機(jī)代碼。編譯原理里面有一個(gè)很重要的內(nèi)容是編譯器優(yōu)化。所謂編譯器優(yōu)化是指,在不改變?cè)瓉?lái)語(yǔ)義的情況下,通過(guò)調(diào)整語(yǔ)句順序,來(lái)讓程序運(yùn)行的更快。這個(gè)過(guò)程成為reorder。要知道,JVM只是一個(gè)標(biāo)準(zhǔn),并不是實(shí)現(xiàn)。JVM中并沒有規(guī)定有關(guān)編譯器優(yōu)化的內(nèi)容,也就是說(shuō),JVM實(shí)現(xiàn)可以自由的進(jìn)展編譯器優(yōu)化。下面來(lái)想一下,創(chuàng)立一個(gè)變量需要哪些步驟呢一個(gè)是申請(qǐng)一塊內(nèi)存,調(diào)用構(gòu)造方法進(jìn)展初始化操作,另一個(gè)是分配一個(gè)指針指向這塊內(nèi)存。這兩個(gè)操作誰(shuí)在前誰(shuí)在后呢JVM標(biāo)準(zhǔn)并沒有規(guī)定。那么就存在這么一種情況,JVM是先開辟出一塊內(nèi)存,然后把指針指向這塊內(nèi)存,最后調(diào)用構(gòu)造方法進(jìn)展初始化。下面我們來(lái)考慮這么一種情況:線程A開場(chǎng)創(chuàng)立SingletonClass的實(shí)例,此時(shí)線程B調(diào)用了getInstance()方法,首先判斷instance是否為null。按照我們上面所說(shuō)的內(nèi)存模型,A已經(jīng)把instance指向了那塊內(nèi)存,只是還沒有調(diào)用構(gòu)造方法,因此B檢測(cè)到instance不為null,于是直接把instance返回了——問(wèn)題出現(xiàn)了,盡管instance不為null,但它并沒有構(gòu)造完成,就像一套房子已經(jīng)給了你鑰匙,但你并不能住進(jìn)去,因?yàn)槔锩孢€沒有收拾。此時(shí),如果B在A將instance構(gòu)造完成之前就是用了這個(gè)實(shí)例,程序就會(huì)出現(xiàn)錯(cuò)誤了!于是,我們想到了下面的代碼:publicclassSingletonClass{
privatestaticSingletonClassinstance=null;
publicstaticSingletonClassgetInstance(){
if(instance==null){
SingletonClasssc;
synchronized(SingletonClass.class){
sc=instance;
if(sc==null){
synchronized(SingletonClass.class){
if(sc==null){
sc=newSingletonClass();
}
}
instance=sc;
}
}
}
returninstance;
}
privateSingletonClass(){
}
}我們?cè)诘谝粋€(gè)同步塊里面創(chuàng)立一個(gè)臨時(shí)變量,然后使用這個(gè)臨時(shí)變量進(jìn)展對(duì)象的創(chuàng)立,并且在最后把instance指針臨時(shí)變量的內(nèi)存空間。寫出這種代碼基于以下思想,即synchronized會(huì)起到一個(gè)代碼屏蔽的作用,同步塊里面的代碼和外部的代碼沒有聯(lián)系。因此,在外部的同步塊里面對(duì)臨時(shí)變量sc進(jìn)展操作并不影響instance,所以外部類在instance=sc;之前檢測(cè)instance的時(shí)候,結(jié)果instance依然是null。不過(guò),這種想法完全是錯(cuò)誤的!同步塊的釋放保證在此之前——也就是同步塊里面——的操作必須完成,但是并不保證同步塊之后的操作不能因編譯器優(yōu)化而調(diào)換到同步塊完畢之前進(jìn)展。因此,編譯器完全可以把instance=sc;這句移到內(nèi)部同步塊里面執(zhí)行。這樣,程序又是錯(cuò)誤的了!6.解決方案說(shuō)了這么多,難道單例沒有方法在Java中實(shí)現(xiàn)嗎其實(shí)不然!在JDK5之后,Java使用了新的內(nèi)存模型。volatile關(guān)鍵字有了明確的語(yǔ)義——在JDK1.5之前,volatile是個(gè)關(guān)鍵字,但是并沒有明確的規(guī)定其用途——被volatile修飾的寫變量不能和之前的讀寫代碼調(diào)整,讀變量不能和之后的讀寫代碼調(diào)整!因此,只要我們簡(jiǎn)單的把instance加上volatile關(guān)鍵字就可以了。publicclassSingletonClass{
privatevolatilestaticSingletonClassinstance=null;
publicstaticSingletonClassgetInstance(){
if(instance==null){
synchronized(SingletonClass.class){
if(instance==null){
instance=newSingletonClass();
}
}
}
returninstance;
}
privateSingletonClass(){
}
}然而,這只是JDK1.5之后的Java的解決方案,那之前版本呢其實(shí),還有另外的一種解決方案,并不會(huì)受到Java版本的影響:publicclassSingletonClass{
privatestaticclassSingletonClassInstance{
privatestaticfinalSingletonClassinstance=newSingletonClass();
}
publicstaticSingletonClassgetInstance
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 玻璃制品市場(chǎng)推廣策略考核試卷
- 紡織面料色彩搭配技巧考核試卷
- 液化石油氣行業(yè)安全生產(chǎn)法律法規(guī)應(yīng)用考核試卷
- 碳排放減少與碳配額管理考核試卷
- 游樂(lè)設(shè)施項(xiàng)目管理中的團(tuán)隊(duì)溝通考核試卷
- 石材表面處理技術(shù)更新考核試卷
- 紡織品的可持續(xù)材料開發(fā)考核試卷
- 米、面制品消費(fèi)行為分析考核試卷
- 電動(dòng)機(jī)在不同負(fù)載下的性能評(píng)估考核試卷
- 景德鎮(zhèn)陶瓷職業(yè)技術(shù)學(xué)院《中國(guó)古代文學(xué)史四明清文學(xué)》2023-2024學(xué)年第二學(xué)期期末試卷
- 云計(jì)算數(shù)據(jù)備份與恢復(fù)預(yù)案
- 人教版七年級(jí)生物上冊(cè)第一單元第一章第二節(jié)生物的特征課件
- 住房城鄉(xiāng)建設(shè)科學(xué)技術(shù)計(jì)劃項(xiàng)目科研開發(fā)類申報(bào)書
- (完整版)英語(yǔ)四級(jí)詞匯表
- 技工學(xué)校國(guó)家助學(xué)金申請(qǐng)表
- GB/T 2424.7-2024環(huán)境試驗(yàn)第3部分:支持文件及導(dǎo)則試驗(yàn)A(低溫)和B(高溫)的溫度箱測(cè)量(帶負(fù)載)
- 重型燃?xì)廨啓C(jī)用大型鑄鍛件 第3部分:鑄鋼件 編制說(shuō)明
- 智慧農(nóng)業(yè)的支撐技術(shù)簡(jiǎn)介
- 政務(wù)服務(wù)中心物業(yè)服務(wù)投標(biāo)方案【新版】(技術(shù)方案)
- 重大事故隱患判定標(biāo)準(zhǔn)培訓(xùn)記錄、培訓(xùn)效果評(píng)估
- 品管圈活動(dòng)在提高腦卒中患者日常基本生活自理技能訓(xùn)練執(zhí)行率的應(yīng)用效果
評(píng)論
0/150
提交評(píng)論