標籤:提升 項目開發 開發人員 環境 最簡 logs 過程 簡單的 精準
單例模式是在平時的項目開發中比較常見的一種設計模式,使用比較普遍,網上的資料也是一抓一大把,小Alan也來湊湊熱鬧,為以後充實點設計模式相關的內容做個簡單的開篇。
單例模式是一種建立對象的模式,用於產生這個類的一個具體的執行個體對象,跟普通的對象建立比起來就那麼一點點區別,區別就在於它可以確保項目中的一個類只會產生一個具體的對象執行個體。而不會出現第二個對象執行個體,第三個對象執行個體。所有使用到這個對象執行個體的地方實際上用的都是同一個對象,這就是所謂的單例模式,對於初學者還可能陌生,對於老司機們來講這可是最最簡單的設計模式之一。
在Java中使用單例模式有哪些好處嘞?
①對於頻繁使用、經常使用的對象,可以省略建立對象也就是沒對象的需要new一個對象所花費的時間,這對於那些重量級的對象來說,還是能少來一個就少來一個,用老對象就好了,減少開銷;
②建立對象的次數變少了,對系統記憶體使用量的頻率就會降低,這樣就減輕了GC的壓力,縮短了GC所耗費的時間;
③開發項目的過程中遇到類只需要一個對象執行個體的時候,那麼就是選擇這模式無疑了。
單例的實現1:
下面給出一個單例的實現,這個實現是so easy的,代碼如下:
1 /** 2 * 單例模式 3 * @author AlanLee 4 * 5 */ 6 public class Singleton 7 { 8 9 // 餓漢模式10 private static Singleton instance = new Singleton();11 12 private Singleton()13 {14 System.out.println("Singleton is create");15 }16 17 public static Singleton getInstance()18 {19 return instance;20 }21 22 }
使用這種方式建立單例對象有幾點需要特別注意。
第一點:我們要保證我們的項目中不會有人意外的建立多餘的對象執行個體的話,我們需要把Singleton的建構函式設定為private私人的。這樣其他開發人員就不能隨便的建立這個類的對象執行個體了,從而避免該類的對象執行個體被錯誤的建立出來;
第二點:instance對象必須是private私人的並且static靜態化的。如果不是private私人的,那麼instance的安全性無法得到保證。一不小心可能就被其他開發人員來個Singleton.instance=xxx的,那這個對象也就被改變了,如果=null的話,可想而知,在使用這個對象執行個體的使用,迎接你的將是Null 物件異常的懷抱。其次,因為Factory 方法getInstance()是static靜態方法,因此方法中返回的變數也得是static的。
探討:每種實現方式在高並發環境下效能如何呢,每種實現方式有木有什麼不足之處?
這個單例模式的實現方式效能是非常好的,因為Factory 方法getInstance()只是簡單的返回instance對象執行個體,並沒有任何鎖操作,因此在並行程式中,還是會有比較不錯的表現滴。
但是這種方式有一點不足,就是instance對象執行個體在什麼時候被建立出來是不受控制的,基礎好點的都知道static成員會在類第一次初始化的時候被建立,這個時候可不一定是Factory 方法getInstance()第一次被調用的時候。
假設你的單例模式是這樣的,代碼如下:
1 /** 2 * 單例模式:執行個體對象第一次初始化的問題 3 * 4 * @author AlanLee 5 * 6 */ 7 public class Singleton2 8 { 9 public static int STATUS = 1;10 11 private static Singleton2 instance = new Singleton2();12 13 private Singleton2()14 {15 System.out.println("Singleton2 is create");16 }17 18 public static Singleton2 getInstance()19 {20 return instance;21 }22 23 }
注意,這個單例還包含另一個靜態成員STATUS。此時,在任何地方引用這個STATUS都會導致instance對象執行個體被建立(任何對Singleton2方法或者欄位的引用,都會導致類初始化,並建立instance執行個體,但是類初始化只有一次,因此instance執行個體永遠只會被建立一次)。
比如:System.out.println(Singleton.STATUS);
上述println會列印出:
可以看到,就算我們沒有要求建立instance單例對象,new Singleton2()也會被調用。
如果不在乎這個小小的不足之處,這種單例模式的實現方式是一種不錯的選擇。它容易實現,代碼易讀而且效能優越。
單例的實現2:
如果你想精準的控制instance的建立時間,那麼就需要使用下面這種方式,一種支援消極式載入的策略,它只會在instance被第一次使用時才會建立對象。代碼如下:
1 /** 2 * 單例模式之懶漢模式 3 * 4 * @author AlanLee 5 * 6 */ 7 public class LazySingleton 8 { 9 private static LazySingleton instance = null;10 11 private LazySingleton()12 {13 System.out.println("LazySingleton is create");14 }15 16 public static synchronized LazySingleton getInstance()17 {18 if (instance == null)19 {20 instance = new LazySingleton();21 }22 return instance;23 }24 }
最初我們並不需要執行個體化instance對象執行個體,只有Factory 方法getInstance()被第一次調用時才會建立單例對象。但是在高並發環境下,為了防止對象被對此建立,我們不得不使用synchronized進行方法同步。這種實現的好處是,充分利用了消極式載入,只有在真正需要時才建立對象。但壞處也很明顯,並發環境下加鎖,在鎖競爭激烈的時候會對效能產生一定的影響。
此外,還有一種被稱為雙重檢查模式的方法可以用於建立單例。這是一種非常醜陋、複雜的方法,甚至在低版本的JDK中都不能保證正確性。不推薦使用,也沒必要在這種方法上花費太多時間。
單例的實現3:
在上述的單例模式實現方式中,可說是各有千秋,那麼第三種方式便是結合兩者的優勢的一種兩全其美的實現方式,代碼如下:
1 /** 2 * 無懈可擊之單例模式 3 * 4 * @author Alanlee 5 * 6 */ 7 public class StaticSingleton 8 { 9 10 private StaticSingleton()11 {12 System.out.println("StaticSingleton is create");13 }14 15 private static class SingletonHolder16 {17 private static StaticSingleton instance = new StaticSingleton();18 }19 20 public static StaticSingleton getInstance()21 {22 return SingletonHolder.instance;23 }24 25 }
上述代碼實現了一個單例模式,並且同時擁有前兩種方式的優點。首先Factory 方法getInstance()沒有使用同步鎖,這使得在高並發環境下效能得到了提升。其次,只有在Factory 方法getInstance()被第一次調用時,StaticSingleton的執行個體才會被建立。這種方式巧妙地使用了內部類和類的初始化方式。內部類SingletonHolder被申明為private私人的,這使得我們不可能在外部存取並初始化它。而我們只能在Factory 方法getInstance()內部對SingletonHolder類進行初始化,利用虛擬機器的類初始化機制建立單例對象。
結束語:寵辱不驚,閑看庭前花開花落;去留無意,漫隨天外雲捲雲舒......小Alan除了喜歡看技術書籍,還是一個武俠玄幻小說愛好者呢!希望自己在IT的這條道路上就能像小說中的主人公一樣,縱然困難重重,亦能化險為夷成就康庄大道,至於坐擁美女環抱啥的,小Alan可不敢想啊,還是有一個深愛自己的女人足以。
可愛博主:AlanLee
部落格地址:http://www.cnblogs.com/AlanLee
本文出自部落格園,歡迎大家加入部落格園。
Java設計模式探討之單例模式