單例模式是一種對象建立模式,用於產生一個類的具體事例。使用單例模式能夠確保整個系統中單例類只產生一個實例。有下面兩大好處:java
- 對於頻繁建立的對象,節省初第一次實例化以後的建立時間。
- 因爲new操做的減小,會下降系統內存的使用頻率。減輕GC壓力,從而縮短GC停頓時間
建立方式:安全
- 單例做爲類的私有private屬性
- 單例類擁有私有private構造函數
- 提供獲取實例的public方法
單例模式的角色:性能優化
角色 | 做用 |
---|---|
單例類 | 提供單例的工廠,返回類的單例實例 |
使用者 | 獲取並使用單例類 |
類基本結構:
多線程
public class HungerSingleton { //1.餓漢式 //私有構造器 private HungerSingleton() { System.out.println("create HungerSingleton"); } //私有單例屬性 private static HungerSingleton instance = new HungerSingleton(); //獲取單例的方法 public static HungerSingleton getInstance() { return instance; } }
注意:jvm
public class Singleton { //2.1簡單懶漢式(線程不安全) //私有構造器 private Singleton() { System.out.println("create Singleton"); } //私有單例屬性[初始化爲null] private static Singleton instance = null; //獲取單例的方法 public static Singleton getInstance() { if(instance == null) { //此處instance實例化 //首次調用單例時會進入 達成延時加載 instance = new Singleton(); } return instance; } }
public class Singleton { //2.2簡單懶漢式(線程安全) //私有構造器 private Singleton() { System.out.println("create Singleton"); } //私有單例屬性[初始化爲null] private static Singleton instance = null; //獲取單例的方法 將此方法使用synchronized關鍵字同步 public static synchronized Singleton getInstance() { if(instance == null) { //此處instance實例化 //首次調用單例時會進入 達成延時加載 instance = new Singleton(); } return instance; } }
面臨的問題:函數
簡單懶漢式(線程安全)中,對getInstance()方法加鎖,致使多線程中性能較差,那麼是否能夠 減少鎖的範圍,使不用每次調用geInstance()方法時候都會去競爭鎖?DCL(Double Check Locking)雙重檢測 就是這樣一種實現方式。性能
public class DCLLazySingleton { //3.DCL //私有構造器 private DCLLazySingleton() { System.out.println("create DCLLazySingleton"); } //私有單例屬性[初始化爲null] volatile 保證內存可見性 防止指令重排 private static volatile DCLLazySingleton instance = null;//step1 //獲取單例的方法 public static DCLLazySingleton getInstance() { //這裏判null 是爲了在instance有值時,不進入加鎖的代碼塊,提升代碼性能。 if(instance == null) { //縮小鎖範圍 因爲是靜態方法方法調用的時候不依賴於實例化的對象 加鎖只能使用類 synchronized (DCLLazySingleton.class) { //這裏判null 是爲了配合volatile解決多線程安全問題 if(instance == null) { instance = new DCLLazySingleton(); } } } return instance; } }
注意:優化
傳統DCL(step1處並未使用 volatile 或使用了但在JDK1.8以前)面臨的問題:spa
解決方式:.net
參考:
public class StaticSingleton { //私有構造器 private StaticSingleton() { System.out.println("create StaticSingleton!"); } //獲取單例的方法 public static StaticSingleton getInstance() { return SingletonHolder.instance; } //靜態內部類 持有單例 做爲靜態屬性。 //因爲只有在訪問屬性時纔會加載靜態類初始化instance。因此實現了懶加載。且因爲JVM保證了類的加載爲線程安全,因此爲線程安全的。 private static class SingletonHolder { //私有單例屬性 private static StaticSingleton instance = new StaticSingleton(); } }
注意:
public enum EnumSingleton { INSTANCE(); EnumSingleton() { System.out.println("create EnumSingleton"); } }
上述的單例實現方式仍是會面臨一些特殊狀況不能保證惟一實例:
private Object readResolve () { //返回當前對象 return instance; }
因爲上述兩狀況比較特殊,因此沒有特別關注。
《Java程序性能優化》 -葛一鳴 等編著