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

微笑面對生活發表於2018-08-06

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。 這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。

優點

1、在記憶體裡只有一個例項,減少了記憶體的開銷,尤其是頻繁的建立和銷燬例項(比如管理學院首頁頁面快取)。

2、避免對資源的多重佔用(比如寫檔案操作)。

缺點

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

實現方式一:懶漢式(執行緒不安全)

懶漢式:就是用的時候再進行例項化物件。

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;
    }
}
複製程式碼

因為它作為靜態資源,所以在類裝載時就被例項化

優點:沒有加鎖,執行效率會提高。

缺點:類載入時就初始化,浪費記憶體。

實現方式四:雙檢鎖/雙重校驗鎖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 種列舉方式。

相關文章