CodeGym /Java Blog /Toto sisi /Java單例類
John Squirrels
等級 41
San Francisco

Java單例類

在 Toto sisi 群組發布
你好!今天,我們將從 Java 單例模式開始,深入探討各種設計模式的細節。讓我們回顧一下:我們對設計模式一般了解多少?設計模式是我們可以用來解決許多已知問題的最佳實踐。設計模式通常不依賴於任何編程語言。將它們視為一組建議,以幫助您避免錯誤並避免重新發明輪子。設計模式:單例 - 1

Java中的單例是什麼?

單例模式是最簡單的類級設計模式之一。有時人們會說“這個類是單例的”,這意味著該類實現了單例設計模式。有時需要編寫一個類,我們將實例化限制為單個對象。例如,一個類負責記錄或連接到一個數據庫。單例設計模式描述了我們如何實現這一點。單例是一種做兩件事的設計模式:
  1. 它保證永遠只有一個類的實例。

  2. 它提供對該實例的單點全局訪問。

因此,幾乎每個單例模式的實現都有兩個特徵:
  1. 私有構造函數。這限制了在類本身之外創建類對象的能力。

  2. 返回類實例的公共靜態方法。此方法稱為getInstance。這是對類實例的全局訪問點。

實施方案

單例設計模式以各種方式應用。每個選項都以其自己的方式是好的和壞的。一如既往,這裡沒有完美的選擇,但我們應該爭取一個。首先,讓我們決定好與壞的構成,以及影響我們如何評估設計模式的各種實現的指標。讓我們從好的開始。以下是使實施更具吸引力和吸引力的因素:
  • 惰性初始化:直到需要時才創建實例。

  • 簡單透明的代碼:這個指標當然是主觀的,但它很重要。

  • 線程安全:在多線程環境下正確運行。

  • 多線程環境中的高性能:共享資源時很少或沒有線程阻塞。

現在的缺點。我們將列出影響實施的因素:
  • 沒有惰性初始化:當應用程序啟動時加載類,無論是否需要它(矛盾的是,在 IT 世界中,惰性更好)

  • 複雜且難以閱讀的代碼。這個指標也是主觀的。如果您的眼睛開始流血,我們會假設實施不是最好的。

  • 缺乏線程安全。換句話說,“線程危險”。多線程環境下的錯誤操作。

  • 多線程環境下的性能不佳:線程在共享資源時一直或經常相互阻塞。

代碼

現在我們準備好考慮各種實施方案並指出優缺點:

簡單的


public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {
    }
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
最簡單的實現。優點:
  • 簡單透明的代碼

  • 線程安全

  • 多線程環境下的高性能

缺點:
  • 沒有惰性初始化。
為了修復前面的缺點,我們得到了第二個實現:

延遲初始化


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
優點:
  • 延遲初始化。

缺點:
  • 不是線程安全的

這個實現很有趣。我們可以延遲初始化,但是我們失去了線程安全。不用擔心——我們在第三個實現中同步所有內容。

同步訪問


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
優點:
  • 延遲初始化。

  • 線程安全

缺點:
  • 多線程性能差

出色的!在第三個實現中,我們恢復了線程安全!當然慢了。。。現在getInstance方法是synchronized的,所以一次只能一個線程執行。我們實際上只需要同步初始化新實例的部分,而不是同步整個方法。但是我們不能簡單地使用同步塊來包裝負責創建新實例的部分。這樣做並不能確保線程安全。這一切都有點複雜。正確的同步可以在下面看到:

雙重檢查鎖定


public class Singleton {
    private static final Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
優點:
  • 延遲初始化。

  • 線程安全

  • 多線程環境下的高性能

缺點:
  • Java 1.5 以下的早期版本不支持(從 1.5 版本開始固定使用 volatile 關鍵字)

請注意,為了使此實施選項正常工作,必須滿足兩個條件之一。INSTANCE變量必須是finalvolatile 我們今天要討論的最後一個實現是類持有者單例

班級持有人


public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
優點:
  • 延遲初始化。

  • 線程安全。

  • 多線程環境下的高性能。

缺點:
  • 正確的操作需要保證單例對象的初始化沒有錯誤。否則,對getInstance方法的第一次調用將導致ExceptionInInitializerError,所有後續調用將產生NoClassDefFoundError

這個實現幾乎是完美的。它是惰性的,線程安全的,而且速度很快。但它有細微差別,如缺點列表中所述。 單例模式的各種實現比較:
執行 延遲初始化 線程安全 多線程性能 什麼時候使用?
簡單的 - + 快速地 絕不。或者可能在延遲初始化不重要時。但永遠不會更好。
延遲初始化 + - 不適用 總是在不需要多線程時
同步訪問 + + 慢的 絕不。或者可能在多線程性能無關緊要時。但永遠不會更好。
雙重檢查鎖定 + + 快速地 在極少數情況下,當您需要在創建單例時處理異常(當類持有者單例不適用時)
班級持有人 + + 快速地 每當需要多線程並且可以保證創建單例對象時不會出現問題。

單例模式的優缺點

通常,單身人士會完全按照預期進行操作:
  1. 它保證永遠只有一個類的實例。

  2. 它提供對該實例的單點全局訪問。

但是,這種模式有缺點:
  1. 單例違反了單一職責原則:單例類除了直接的職責外,還控制著實例的數量。

  2. 普通類對單例的依賴在類的公共契約中是不可見的。

  3. 全局變量不好。最終,單例會變成一個龐大的全局變量。

  4. 單例的存在降低了整個應用程序的可測試性,尤其是使用單例的類。

就是這樣!:) 我們已經與您探討了 Java 單例類。現在,在你的餘生中,當與你的程序員朋友交談時,你不僅可以提及模式有多好,還可以說幾句它的壞處。祝你好運掌握這些新知識。

補充閱讀:

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION