(轉(zhuǎn))單例模式的記錄

第一種(懶漢达吞,線程不安全):

public class Singleton {  
   private static Singleton instance;  
   private Singleton (){}  
 
   public static Singleton getInstance() {  
   if (instance == null) {  
       instance = new Singleton();  
   }  
   return instance;  
   }  
}  

這種寫(xiě)法lazy loading很明顯培他,但是致命的是在多線程不能正常工作。
第二種(懶漢草添,線程安全):

public class Singleton {  
   private static Singleton instance;  
   private Singleton (){}  
   public static synchronized Singleton getInstance() {  
   if (instance == null) {  
       instance = new Singleton();  
   }  
   return instance;  
   }  
}  

這種寫(xiě)法能夠在多線程中很好的工作驶兜,而且看起來(lái)它也具備很好的lazy loading,但是远寸,遺憾的是抄淑,效率很低,99%情況下不需要同步驰后。
第三種(餓漢):

public class Singleton {  
   private static Singleton instance = new Singleton();  
   private Singleton (){}  
   public static Singleton getInstance() {  
   return instance;  
   }  
}  

這種方式基于classloder機(jī)制避免了多線程的同步問(wèn)題肆资,不過(guò),instance在類裝載時(shí)就實(shí)例化灶芝,雖然導(dǎo)致類裝載的原因有很多種郑原,在單例模式中大多數(shù)都是調(diào)用getInstance方法, 但是也不能確定有其他的方式(或者其他的靜態(tài)方法)導(dǎo)致類裝載夜涕,這時(shí)候初始化instance顯然沒(méi)有達(dá)到lazy loading的效果犯犁。
第四種(漢,變種):

public class Singleton {  
   private Singleton instance = null;  
   static {  
   instance = new Singleton();  
   }  
   private Singleton (){}  
   public static Singleton getInstance() {  
   return this.instance;  
   }  
}  

表面上看起來(lái)差別挺大女器,其實(shí)更第三種方式差不多酸役,都是在類初始化即實(shí)例化instance。
第五種(靜態(tài)內(nèi)部類):

public class Singleton {  
   private static class SingletonHolder {  
   private static final Singleton INSTANCE = new Singleton();  
   }  
   private Singleton (){}  
   public static final Singleton getInstance() {  
   return SingletonHolder.INSTANCE;  
   }  
}  

這種方式同樣利用了classloder的機(jī)制來(lái)保證初始化instance時(shí)只有一個(gè)線程驾胆,它跟第三種和第四種方式不同的是(很細(xì)微的差別):第三種和第四種方式是只要Singleton類被裝載了涣澡,那么instance就會(huì)被實(shí)例化(沒(méi)有達(dá)到lazy loading效果),而這種方式是Singleton類被裝載了丧诺,instance不一定被初始化入桂。因?yàn)镾ingletonHolder類沒(méi)有被主動(dòng)使用,只有顯示通過(guò)調(diào)用getInstance方法時(shí)驳阎,才會(huì)顯示裝載SingletonHolder類抗愁,從而實(shí)例化instance惕艳。想象一下,如果實(shí)例化instance很消耗資源驹愚,我想讓他延遲加載远搪,另外一方面,我不希望在Singleton類加載時(shí)就實(shí)例化逢捺,因?yàn)槲也荒艽_保Singleton類還可能在其他的地方被主動(dòng)使用從而被加載谁鳍,那么這個(gè)時(shí)候?qū)嵗痠nstance顯然是不合適的。這個(gè)時(shí)候劫瞳,這種方式相比第三和第四種方式就顯得很合理倘潜。
第六種(枚舉):

public enum Singleton {  
   INSTANCE;  
   public void whateverMethod() {  
   }  
}  

這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問(wèn)題志于,而且還能防止反序列化重新創(chuàng)建新的對(duì)象涮因,可謂是很堅(jiān)強(qiáng)的壁壘啊,不過(guò)伺绽,個(gè)人認(rèn)為由于1.5中才加入enum特性养泡,用這種方式寫(xiě)不免讓人感覺(jué)生疏,在實(shí)際工作中奈应,我也很少看見(jiàn)有人這么寫(xiě)過(guò)澜掩。
第七種(雙重校驗(yàn)鎖):

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  

這個(gè)是第二種方式的升級(jí)版,俗稱雙重檢查鎖定杖挣,詳細(xì)介紹請(qǐng)查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html
在JDK1.5之后肩榕,雙重檢查鎖定才能夠正常達(dá)到單例效果。

總結(jié)
有兩個(gè)問(wèn)題需要注意:
1.如果單例由不同的類裝載器裝入惩妇,那便有可能存在多個(gè)單例類的實(shí)例株汉。假定不是遠(yuǎn)端存取,例如一些servlet容器對(duì)每個(gè)servlet使用完全不同的類裝載器歌殃,這樣的話如果有兩個(gè)servlet訪問(wèn)一個(gè)單例類乔妈,它們就都會(huì)有各自的實(shí)例。
2.如果Singleton實(shí)現(xiàn)了java.io.Serializable接口挺份,那么這個(gè)類的實(shí)例就可能被序列化和復(fù)原褒翰。不管怎樣,如果你序列化一個(gè)單例類的對(duì)象匀泊,接下來(lái)復(fù)原多個(gè)那個(gè)對(duì)象,那你就會(huì)有多個(gè)單例類的實(shí)例朵你。
對(duì)第一個(gè)問(wèn)題修復(fù)的辦法是:

private static Class getClass(String classname)      
                                         throws ClassNotFoundException {     
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
      
      if(classLoader == null)     
         classLoader = Singleton.class.getClassLoader();     
      
      return (classLoader.loadClass(classname));     
   }     
}  

對(duì)第二個(gè)問(wèn)題修復(fù)的辦法是:

public class Singleton implements java.io.Serializable {     
  public static Singleton INSTANCE = new Singleton();     
     
  protected Singleton() {     
       
  }     
  private Object readResolve() {     
           return INSTANCE;     
     }    
}   

對(duì)我來(lái)說(shuō)各聘,我比較喜歡第三種和第五種方式,簡(jiǎn)單易懂抡医,而且在JVM層實(shí)現(xiàn)了線程安全(如果不是多個(gè)類加載器環(huán)境)躲因,一般的情況下早敬,我會(huì)使用第三種方式,只有在要明確實(shí)現(xiàn)lazy loading效果時(shí)才會(huì)使用第五種方式大脉,另外搞监,如果涉及到反序列化創(chuàng)建對(duì)象時(shí)我會(huì)試著使用枚舉的方式來(lái)實(shí)現(xiàn)單例,不過(guò)镰矿,我一直會(huì)保證我的程序是線程安全的琐驴,而且我永遠(yuǎn)不會(huì)使用第一種和第二種方式,如果有其他特殊的需求秤标,我可能會(huì)使用第七種方式绝淡,畢竟,JDK1.5已經(jīng)沒(méi)有雙重檢查鎖定的問(wèn)題了苍姜。

不過(guò)一般來(lái)說(shuō)牢酵,第一種不算單例,第四種和第三種就是一種衙猪,如果算的話馍乙,第五種也可以分開(kāi)寫(xiě)了。所以說(shuō)垫释,一般單例都是五種寫(xiě)法潘拨。懶漢,惡漢饶号,雙重校驗(yàn)鎖铁追,枚舉和靜態(tài)內(nèi)部類。
轉(zhuǎn)載請(qǐng)注明出處:http://cantellow.iteye.com/blog/838473

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茫船,一起剝皮案震驚了整個(gè)濱河市琅束,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌算谈,老刑警劉巖涩禀,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異然眼,居然都是意外死亡艾船,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)高每,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屿岂,“玉大人,你說(shuō)我怎么就攤上這事鲸匿∫常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵带欢,是天一觀的道長(zhǎng)运授。 經(jīng)常有香客問(wèn)我烤惊,道長(zhǎng),這世上最難降的妖魔是什么吁朦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任柒室,我火速辦了婚禮,結(jié)果婚禮上逗宜,老公的妹妹穿的比我還像新娘雄右。我一直安慰自己,他們只是感情好锦溪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布不脯。 她就那樣靜靜地躺著,像睡著了一般刻诊。 火紅的嫁衣襯著肌膚如雪防楷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天则涯,我揣著相機(jī)與錄音复局,去河邊找鬼。 笑死粟判,一個(gè)胖子當(dāng)著我的面吹牛亿昏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播档礁,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼角钩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呻澜?” 一聲冷哼從身側(cè)響起递礼,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎羹幸,沒(méi)想到半個(gè)月后脊髓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡栅受,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年将硝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屏镊。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡依疼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闸衫,到底是詐尸還是另有隱情涛贯,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布蔚出,位于F島的核電站弟翘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏骄酗。R本人自食惡果不足惜稀余,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望趋翻。 院中可真熱鬧睛琳,春花似錦、人聲如沸踏烙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)讨惩。三九已至辟癌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荐捻,已是汗流浹背黍少。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留处面,地道東北人厂置。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像魂角,于是被迫代替她去往敵國(guó)和親昵济。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容