單例模式(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 種列舉方式。