23種設計模式之單例模式

單例模式

單例模式分八種方式

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工廠)。線程

總結:在使用單例模式時,考慮的問題無非就是線程是否安全、是否延遲加載、效率是否高等問題。當線程安全、延遲加載、效率較高時,該方式的單例模式就可用。

相關文章
相關標籤/搜索