1)餓漢式(靜態常量)
2)餓漢式(靜態代碼塊)
3)懶漢式(線程不安全)
4)懶漢式(線程安全,同步方法)
5)懶漢式(線程安全,同步代碼塊)
6)雙重檢查
7)靜態內部類
8)枚舉java
public class SingletonTest01 { public static void main(String[] args) { // 測試 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // true } } /** * 餓漢式(靜態變量) * */ class Singleton { /** * 構造函數私有化,外部不能new * @param [] * @date 2019/7/28 9:50 **/ private Singleton() { } /** * 本類內部建立實例對象 * */ private final static Singleton instance = new Singleton(); /** * 對外提供接口獲取對象 * @param [] * @date 2019/7/28 9:51 * @return Singleton **/ public static Singleton getInstance() { return instance; } }
優缺點說明:
1)優勢:這種寫法比較簡單,就是在類加載的時候就完成實例化。避免了線程同步問題。
2)缺點:在類加載的時候就完成實例化,沒有達到Lazy Loading(懶加載)的效果。若是從始至終從未使用過這個實例,則會形成內存的浪費。
3)結論:這種方式可用,可能形成內存浪費。數據庫
public class SingletonTest02 { public static void main(String[] args) { // 測試 Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1 == singleton2); // true } } /** * 餓漢式(靜態代碼塊) * */ class Singleton { /** * 構造函數私有化,外部不能new * @param [] * @date 2019/7/28 9:50 **/ private Singleton() { } /** * 本類內部建立實例對象 * */ private static Singleton instance; /** * 在靜態代碼塊中實例化對象 * */ static { instance = new Singleton(); } /** * 對外提供接口獲取對象 * @param [] * @date 2019/7/28 9:51 * @return Singleton **/ public static Singleton getInstance() { return instance; } }
優缺點:
1)這種方式和上面的方式相似的,優缺點一致。
2)結論:這種方式可用,但可能會形成內存浪費。安全
public class SingletonTest03 { public static void main(String[] args) { // 測試 Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1 == singleton2); // true } } /** * 懶漢式(線程不安全) * */ class Singleton { /** * 建立未初始化的對象 * */ private static Singleton instance; /** * 構造函數私有化 * */ private Singleton() { } /** * 對外提供接口並實例化對象 * 當使用該方法時,才實例化對象 * */ public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
優缺點:
1)起到了Lazy Loading的效果,可是隻能在單線程的環境下使用。
2)若是在多線程下,一個線程進入if(singleton == null)判斷語句塊,還將來得及往下執行,另外一個線程也經過了判斷語句,這是就會形成多實例現象。
3)結論:在實際開發中,不可以使用。session
public class SingletonTest04 { public static void main(String[] args) { // 測試 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } /** * 懶漢式(同步方法,線程安全) * */ class Singleton { /** * 建立未實例化對象 * */ private static Singleton instance; /** * 構造函數私有化 * */ private Singleton(){} /** * 在同步鎖下,進行判斷實例化對象 * @param [] * @date 2019/7/28 10:33 * @return Singleton **/ public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
優缺點:
1)解決了線程不安全問題。
2)效率過低,每一個線程在想獲取實例時,執行getInstance()方法都要進行同步。而其實這個方法只要執行一次就行,後面想獲取該類實例,直接return就好了。
3)結論:在實際開發中,不可用。多線程
public class SingletonTest05 { public static void main(String[] args) { // 測試 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } /** * 懶漢式(同步代碼塊,線程不安全) * */ class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { instance = new Singleton(); } } return instance; } }
優缺點:
1)這種方式本意是爲了解決第四種方式效率低的問題。
2)可是這種同步並不能起到線程同步的做用,和第三種方式遇到的狀況相似。
3)結論:在實際開發中,不可以使用。函數
public class SingletonTest06 { public static void main(String[] args) { // 校驗 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // true } } /** * 雙重校驗 * */ class Singleton { /** 建立未實例化對象 */ private static volatile Singleton instance; /** 構造函數私有化 */ private Singleton() {} /** * 進行雙重校驗,解決線程安全問題,同時解決懶加載問題,而且解決了效率問題 * @param [] * @date 2019/7/28 10:55 * @return Singleton **/ public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
優缺點:
1)Double-Check概念是多線程開發中常常使用的方式,如代碼中所示,經過兩重if(instance == null)檢查,保證了線程安全。
2)這樣,實例化代碼只要執行一次,後面再次訪問就直接返回實例化對象,避免了重複的進行方法同步。
3)線程安全,延遲加載,效率較高。
4)結論:在實際開發中,推薦使用。工具
public class SingletonTest07 { public static void main(String[] args) { // 測試 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // true } } /** * 靜態內部類 * */ class Singleton { /** * 構造函數私有化 * */ private Singleton() {} /** * 靜態內部類,該類中實例化Singleton屬性 * */ private static class SingletonInstance { private final static Singleton INSTANCE = new Singleton(); } /** * 對外提供接口獲取實例化對象 * */ public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
優缺點:
1)這種方式利用類裝載機制保證了在初始化實例時只有一個線程。
2)靜態內部類方式在Singleton類被裝載時不會當即實例化,而是在須要實例化時,調用getInstance()方法,纔會裝載SingletonInstance類,從而完成了Singleton的實例化。
3)類的靜態屬性只會在第一次加載類時初始化,因此在這裏,JVM幫助咱們保證了線程安全。
4)優勢:避免了線程不安全,利用靜態內部類特色實現了延遲加載,而且效率較高。
5)結論:在實際開發中,推薦使用。性能
public class SingletonTest08 { public static void main(String[] args) { Singleton s1 = Singleton.INSTANCE; Singleton s2 = Singleton.INSTANCE; System.out.println(s1 == s2); // true s1.sayOk(); } } /** * 枚舉 * */ enum Singleton { /** * 屬性 * */ INSTANCE; public void sayOk() { System.out.println("ok"); } }
優缺點:
1)經過枚舉方式實現的單例模式。不只能夠避免多線程問題,並且還能防止反序列化從新建立對象。
2)這種方式也是Effective Java做者提倡的方式。
3)結論:推薦使用。測試
1)單例模式保證了系統內存中該類只存在一個對象,節省了系統資源,對於一些頻繁建立銷燬對象,使用單例模式能夠提升系統性能。
2)單例模式使用場景:須要頻繁的進行建立和銷燬對象、建立對象時耗時過多或耗費資源過多(即重量級對象),但又常常使用的、工具類對象、頻繁訪問數據庫或文件的對象(數據源、session工廠)。線程
總結:在使用單例模式時,考慮的問題無非就是線程是否安全、是否延遲加載、效率是否高等問題。當線程安全、延遲加載、效率較高時,該方式的單例模式就可用。