單例模式(Singleton):
1 public class Singleton { 2 private static Singleton instance = new Singleton(); 3 4 public static Singleton getInstance() { 5 return instance; 6 } 7 }
優點:
1.線程安全
2.在類加載的同時已經創建好一個靜態對象,調用時反應速度快
缺點:
資源效率不高,可能getInstance()永遠不會執行到,但執行該類的其他靜態方法或者加載了該類(class.forName),那么這個實例仍然初始化
(二)普通的懶漢式:
1 public class Singleton { 2 3 /* 持有私有靜態實例,防止被引用,此處賦值為null,目的是實現延遲加載 */ 4 private static Singleton instance = null; 5 6 /* 私有構造方法,防止被實例化 */ 7 private Singleton() { 8 } 9 10 /* 靜態工程方法,創建實例 */ 11 public static Singleton getInstance() { 12 if (instance == null) { 13 instance = new Singleton(); 14 } 15 return instance; 16 } 17 18 /* 如果該對象被用於序列化,可以保證對象在序列化前后保持一致 */ 19 public Object readResolve() { 20 return instance; 21 } 22 }
這個類可以滿足基本要求,但是,像這樣毫無線程安全保護的類,如果我們把它放入多線程的環境下,肯定就會出現問題了,instance對象被多條語句所操作(判斷為空和創建實例);所以該類需要優化,可以加同步來解決,而加同步的方式使用同步函數和同步代碼塊都行,但稍微有些低效,用雙重判斷的方式能解決效率問題:
(三)優化后的懶漢式:
1 public static Singleton getInstance() { 2 if (instance == null) { 3 synchronized (Singleton.class) { 4 if (instance == null) { 5 instance = new Singleton(); 6 } 7 } 8 } 9 return instance; 10 }
(四)靜態內部類:
1 public class Singleton { 2 3 /* 私有構造方法,防止被實例化 */ 4 private Singleton() { 5 } 6 7 /* 此處使用一個內部類來維護單例 */ 8 private static class SingletonFactory { 9 private static Singleton instance = new Singleton(); 10 } 11 12 /* 獲取實例 */ 13 public static Singleton getInstance() { 14 return SingletonFactory.instance; 15 } 16 17 /* 如果該對象被用於序列化,可以保證對象在序列化前后保持一致 */ 18 public Object readResolve() { 19 return getInstance(); 20 } 21 }
這種方式同樣利用了classloder的機制來保證初始化instance時只有一個線程,這種方式是Singleton類被裝載了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有調用getInstance方法時,才會顯示裝載SingletonHolder類,從而實例化instance。想象一下,如果實例化instance很消耗資源,我想讓他延遲加載,另外一方面,我不希望在Singleton類加載時就實例化,因為我不能確保Singleton類還可能在其他的地方被主動使用從而被加載,那么這個時候實例化instance顯然是不合適的。這個時候,這種方式就顯得很合理。注意:如果Singleton實現了java.io.Serializable接口,那么這個類的實例就可能被序列化和復原。不管怎樣,如果你序列化一個單例類的對象,接下來復原多個那個對象,那你就會有多個單例類的實例。而上述代碼17行就是解決這個問題的方法.
實際情況中,單例模式使用內部類來維護單例的實現,JVM內部的機制能夠保證當一個類被加載的時候,這個類的加載過程是線程互斥的。這樣當我們第一次調用getInstance的時候,JVM能夠幫我們保證instance只被創建一次,並且會保證把賦值給instance的內存初始化完畢,這樣我們就不用擔心上面的問題。同時該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了低性能問題。但是,如果在構造函數中拋出異常,實例將永遠得不到創建,也會出錯。
(五)枚舉
1 1.public enum Singleton { 2 2. INSTANCE; 3 3. public void whateverMethod() { 4 4. } 5 5.}
這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象,可謂是很堅強的壁壘啊,不過,個人認為由於1.5中才加入enum特性,用這種方式寫不免讓人感覺生疏,在實際工作中,我也很少看見有人這么寫過。
總結: