設計模式快速學習(三)單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。 這種模式涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問其惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。編程

優勢

一、在內存裏只有一個實例,減小了內存的開銷,尤爲是頻繁的建立和銷燬實例(好比管理學院首頁頁面緩存)。設計模式

二、避免對資源的多重佔用(好比寫文件操做)。緩存

缺點

沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。安全

實現方式一:懶漢式(線程不安全)

懶漢式:就是用的時候再進行實例化對象。bash

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}
複製代碼

這種實現方式不支持多線程,由於沒有同步鎖,多線程下不能正常工做。多線程

實現方式二:懶漢式(線程安全)

public class Singleton {
    private static Singleton instance;

    public static synchronized Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
複製代碼

能夠在多線程環境下使用,可是效率過低。併發

優勢:一個對象初始化一次,節省內存。性能

缺點:必須用synchronized來維持單例,沒效率。測試

實現方式三:餓漢式(線程安全)

public class Singleton {
    private static Singleton instance = new Singleton();

    public static Singleton getInstance(){
        return instance;
    }
}
複製代碼

由於它做爲靜態資源,因此在類裝載時就被實例化ui

優勢:沒有加鎖,執行效率會提升。

缺點:類加載時就初始化,浪費內存。

實現方式四:雙檢鎖/雙重校驗鎖DCL(線程安全)

public class Singleton {
    private static Singleton instance;

    public static Singleton getInstance(){
        if (instance == null){
            synchronized (Singleton.class){
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
複製代碼

採用雙鎖機制,安全且在多線程狀況下能保持高性能。詳細瞭解請點擊:Java併發編程 -- 單例模式線程安全問題

實現方式五:登記式/靜態內部類(線程安全)

public class Singleton {
    private static Singleton instance;

    private static class SingletonHandler{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return SingletonHandler.INSTANCE;
    }
}
複製代碼

這種方式能夠說是惡漢式的變通版,SingletonHandler沒有被主動使用的狀況下是不會實例化Singleton對象的,因此這樣作,既能達到lazy式的加載,又能保證線程安全。

實現方式六:枚舉類(線程安全)

public enum  Singleton {
    INSTANCE;
    public void myMethod() {
        System.out.println("enum instance test");
    }
}
複製代碼

它不只能避免多線程同步問題,並且還自動支持序列化機制,防止反序列化從新建立新的對象,絕對防止屢次實例化。

測試:

public class Main {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        singleton.myMethod();
    }
}
複製代碼
enum instance test
複製代碼

總結

不建議使用第 1 種和第 2 種懶漢方式,建議使用第 3 種餓漢方式。只有在要明確實現 lazy loading 效果時,纔會使用第 5 種登記方式。若是涉及到反序列化建立對象時,能夠嘗試使用第 6 種枚舉方式。

相關文章
相關標籤/搜索