代碼參考:《重學Java設計模式·小傅哥》
目錄
- 1、靜態類使用
- 2、懶漢模式(線程不安全)
- 3、懶漢模式(線程安全)
- 4、餓漢模式(線程安全)
- 5、使用類的內部類(線程安全)
- 6、雙重鎖檢驗(線程安全)
- 7、CAS[AtomicReference](線程安全)
- 8、枚舉單例(線程安全)
在單例模式的實現上,可以考慮以下幾點:
1、是否支持懶漢模式
2、是否線程安全
1、靜態類使用
public class Singleton_00 {public static Map<String, String> cache = new ConcurrentHashMap<String, String> ();
}
使用靜態類的方式可以在第一次運行時就直接初始化Map類,同時也不需要延遲加載使用。
僅用于全局訪問,使用方便。
如果需要被繼承或者維持一些特定狀態,則不能使用。
這個方法不屬于單例模式,但是蘊含單例模式思想
2、懶漢模式(線程不安全)
public class Singleton_01 {private static Singleton_01 instance;private Singleton_01() {}public static Singleton_01 getInstance() {if (null != instance) return instance;instance = new Singleton_01();return instance;}
}
單例模式的特點就是不允許外部直接創建,也就是構造函數腰圍private。
此方式單例滿足了懶漢加載,但是如果有多個訪問者同時去獲取對象實例會造成多同樣的實例并存,沒有達成單例要求。
3、懶漢模式(線程安全)
public class Singleton_02 {private static Singleton_02 instance;private Singleton_02() {}public static synchronized Singleton_02 getInstance() {if (null != instance) return instance;instance = new Singleton_02();return instance;}
}
此模式保證了線程安全,但是由于把鎖加到了方法上,所有的訪問都因為需要鎖占用導致資源浪費。
4、餓漢模式(線程安全)
public class Singleton_03 {private static Singleton_03 instance = new Singleton_03();private Singleton_03() {}public static Singleton_03 getInstance() {return instance;}
}
此模式在程序啟動時直接運行加載,后續有外部需要使用的時候獲取即可。
無論程序中是否用到這樣的類都會在程序啟動之初創建。(這也是它的缺點,無意義地占用內存)
5、使用類的內部類(線程安全)
public class Singleton_04 {private static class SingletonHolder {private static Singleton_04 instance = new Singleton_04();}private Singleton_04() {}public static Singleton_04 getInstance() {return SingletonHolder.instance;}
}
使用類的靜態內部類實現的單例模式,保證了線程安全,也保證了懶加載,也不會因為加索的方式耗費性能
這主要是因為JVM虛擬機保證多線程并發訪問正確性,一個類的構造方法在多線程環境可以被正確加載
推薦使用
6、雙重鎖檢驗(線程安全)
public class Singleton_05 {private static Singleton_05 instance;private Singleton_05() {}public static Singleton_05 getInstance() {if (null != instance) return instance;synchronized (Singleton_05.class) {if (null == instance)instance = new Singleton_05();}return instance;}
}
雙重鎖的方式是方法級鎖的優化,減少了部分獲取實例的耗時,同時這種方法也滿足懶加載
7、CAS[AtomicReference](線程安全)
public class Singleton_06 {private static final AtomicReference<Singleton_06> INSTANCE = new AtomicReference<Singleton_06>();private static Singleton_06 instance;private Singleton_06() {}public static final Singleton_06 getInstance() {for (;;){Singleton_06 instance = INSTANCE.get();if (null != instance) return instance;INSTANCE.compareAndSet(null, new Singleton_06());return INSTANCE.get();}}public static void main(String[] args) {System.out.printIn(Singleton_06.getInstance());System.out.printIn(Singleton_06.getInstance());}
}
java并發庫提供了很多原子類支持并發訪問數據安全性,AtomicReference可以封裝引用一個實例,支持并發訪問。
使用CAS的好處就是不需要使用傳統的加鎖方式保證線程安全,而是依賴于CAS的忙等算法,依賴于底層硬件的時間。
相對于其他鎖的實現沒有線程的切換和組測也就沒有了額外的開銷,可以支持比較大的并發性
缺點就是如果一直沒有獲取到將會處于死循環中。
8、枚舉單例(線程安全)
public enum Singleton_07 {INSTANCE;public void test() {System.out.printIn("hi~");}
}
調用方式:
public void test() {Singleton_07.INSTANCE.test();
}
這種方式在功能上與共有域方法相近,但更加簡潔,無償提供串行化機制,絕對防止對此實例化。
但是在繼承場景下不可用