單例模式(Singleton Pattern):確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例锥咸,這個類稱為單例類靶溜,它提供全局訪問的方法。單例模式是一種對象創(chuàng)建型模式录煤。
單例模式有三個要點:一是某個類只能有一個實例;二是它必須自行創(chuàng)建這個實例荞胡;三是它必須自行向整個系統(tǒng)提供這個實例妈踊。
單例模式是結構最簡單的設計模式一,在它的核心結構中只包含一個被稱為單例類的特殊類泪漂。
單例模式結構圖中只包含一個單例角色:
Singleton(單例):在單例類的內部實現(xiàn)只生成一個實例廊营,同時它提供一個靜態(tài)的getInstance()工廠方法,讓客戶可以訪問它的唯一實例萝勤;為了防止在外部對其實例化露筒,將其構造函數(shù)設計為私有;在單例類內部定義了一個Singleton類型的靜態(tài)對象敌卓,作為外部共享的唯一實例慎式。
主要優(yōu)點:
1、提供了對唯一實例的受控訪問趟径。
2瘪吏、由于在系統(tǒng)內存中只存在一個對象,因此可以節(jié)約系統(tǒng)資源蜗巧,對于一些需要頻繁創(chuàng)建和銷毀的對象單例模式無疑可以提高系統(tǒng)的性能掌眠。
3、允許可變數(shù)目的實例惧蛹。
主要缺點:
1扇救、由于單利模式中沒有抽象層刑枝,因此單例類的擴展有很大的困難香嗓。
2、單例類的職責過重装畅,在一定程度上違背了“單一職責原則”靠娱。
3、濫用單例將帶來一些負面問題掠兄,如為了節(jié)省資源將數(shù)據庫連接池對象設計為的單例類像云,可能會導致共享連接池對象的程序過多而出現(xiàn)連接池溢出锌雀;如果實例化的對象長時間不被利用,系統(tǒng)會認為是垃圾而被回收迅诬,這將導致對象狀態(tài)的丟失腋逆。
適用場景
在以下情況下可以考慮使用單例模式:
- (1) 系統(tǒng)只需要一個實例對象,如系統(tǒng)要求提供一個唯一的序列號生成器或資源管理器侈贷,或者需要考慮資源消耗太大而只允許創(chuàng)建一個對象惩歉。
- (2) 客戶調用類的單個實例只允許使用一個公共訪問點,除了該公共訪問點俏蛮,不能通過其他途徑訪問該實例撑蚌。
餓漢式單例類與懶漢式單例類比較
餓漢式單例類在類被加載時就將自己實例化,它的優(yōu)點在于無須考慮多線程訪問問題搏屑,可以確保實例的唯一性争涌;從調用速度和反應時間角度來講,由于單例對象一開始就得以創(chuàng)建辣恋,因此要優(yōu)于懶漢式單例亮垫。但是無論系統(tǒng)在運行時是否需要使用該單例對象,由于在類加載時該對象就需要創(chuàng)建伟骨,因此從資源利用效率角度來講包警,餓漢式單例不及懶漢式單例,而且在系統(tǒng)加載時由于需要創(chuàng)建餓漢式單例對象底靠,加載時間可能會比較長害晦。
懶漢式單例類在第一次使用時創(chuàng)建,無須一直占用系統(tǒng)資源暑中,實現(xiàn)了延遲加載壹瘟,但是必須處理好多個線程同時訪問的問題,特別是當單例類作為資源控制器鳄逾,在實例化時必然涉及資源初始化稻轨,而資源初始化很有可能耗費大量時間,這意味著出現(xiàn)多線程同時首次引用此類的機率變得較大雕凹,需要通過雙重檢查鎖定等機制進行控制殴俱,這將導致系統(tǒng)性能受到一定影響。
一種更好的單例實現(xiàn)方法
餓漢式單例類不能實現(xiàn)延遲加載枚抵,不管將來用不用始終占據內存线欲;懶漢式單例類線程安全控制煩瑣,而且性能受影響汽摹±罘幔可見,無論是餓漢式單例還是懶漢式單例都存在這樣那樣的問題逼泣,有沒有一種方法趴泌,能夠將兩種單例的缺點都克服舟舒,而將兩者的優(yōu)點合二為一呢?答案是:Yes嗜憔!下面我們來學習這種更好的被稱之為Initialization on Demand Holder (IoDH)的技術秃励。
在IoDH中,我們在單例類中增加一個靜態(tài)(static)內部類吉捶,在該內部類中創(chuàng)建單例對象莺治,再將該單例對象通過getInstance()方法返回給外部使用,實現(xiàn)代碼如下所示:
//Initialization on Demand HolderpublicclassSingleton{privateSingleton(){ ? ? ? }privatestaticclassHolderClass ? ? ? {privatefinalstaticSingleton ?instance =newSingleton(); ? ? ? }publicstaticSingletongetInstance() ? ? ? {returnHolderClass.instance; ? ? ? }publicstaticvoidmain(String args[]) ? ? ? { ? ? ? ? ? ? ?Singleton ?s1, s2;s1 = Singleton.getInstance(); ? ? ? ? ? ? ?s2 ?= Singleton.getInstance(); ? ? ? ? ? ? ?System.out.println(s1==s2); ? ? ? }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
編譯并運行上述代碼帚稠,運行結果為:true谣旁,即創(chuàng)建的單例對象s1和s2為同一對象。由于靜態(tài)單例對象沒有作為Singleton的成員變量直接實例化滋早,因此類加載時不會實例化Singleton榄审,第一次調用getInstance()時將加載內部類HolderClass,在該內部類中定義了一個static類型的變量instance杆麸,此時會首先初始化這個成員變量搁进,由Java虛擬機來保證其線程安全性,確保該成員變量只能初始化一次昔头。由于getInstance()方法沒有任何線程鎖定饼问,因此其性能不會造成任何影響。
通過使用IoDH揭斧,我們既可以實現(xiàn)延遲加載莱革,又可以保證線程安全,不影響系統(tǒng)性能讹开,不失為一種最好的Java語言單例模式實現(xiàn)方式(其缺點是與編程語言本身的特性相關盅视,很多面向對象語言不支持IoDH)。