標籤:span 構造 vat 實現 擷取 影響 最佳化 第一個 關鍵字
一,最簡單的方式
public class Singleton{ private Singleton(){}; private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; }}
首先建構函式聲明為private,防止被外部建立該類的執行個體。聲明一個static的成員變數instance並分配執行個體,當Singleton類被載入時,instance便會被建立,可以通過靜態方法getInstance方法擷取到該執行個體。
優點是實現簡單,且沒有安全執行緒問題。缺點是Singleton被引用時instance執行個體就已經建立,即使執行個體並沒有用到。
二,懶載入的單例類
public class Singleton{ private Singleton(){}; private static Singleton instance = null; public static synchronized Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; }}
因為instance初始值為null,所以在Singleton被載入的時候,並不會執行個體化,只有調用getInstance方法的時候才會建立執行個體。為了防止多線程並發調用getInstance方法時instance被多次建立,所以使用synchronized關鍵字進行線程同步。
該實現缺點是,每次調用getInstance方法都要進行線程同步,影響並發量。
三,改進懶載入的單例類
public class Singleton{ private Singleton(){}; private static volatile Singleton instance = null; public static Singleton getInstance(){ if(instance == null){ synchronized(Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; }}
通過雙檢索的方式進行最佳化,只有在intance沒有值時才進行線程同步,此後會直接返回執行個體。需要注意的是,在synchronized代碼塊中需要再判斷一次instance是否為null,防止多個線程同時通過了第一個為null的判斷。
還需要注意的是,instance需要volatile修飾,防止指令重排序導致的錯誤。
volatile除了保證線程緩衝及時同步到主記憶體並清理其他線程緩衝的值,還有一個作用就是防止指令重排序。 instance = new Singleton() 這行代碼編譯後會拆分成三個指令,可以理解成如下代碼:
1,Singleton temp = malloc(); // 分配記憶體
2,constructor(temp); // 調用建構函式對分配的記憶體進行初始化
3,instance = temp; // 初始化完成的記憶體位址賦值給instance
編譯器為了最佳化指令,重排序後,可能會變成了下面的代碼:
1,Singleton temp = malloc(); // 分配記憶體
2,instance = temp; // 初始化完成的記憶體位址賦值給instance
3,constructor(instance); // 調用建構函式對分配的記憶體進行初始化
如果代碼執行了上面的第二步,instance已經賦值不為null,但並沒有初始化,這是如果第二個線程調用getInstance方法就會直接獲得instance,調用instance時引發錯誤。volatile可以防止重排序。
四,通過內部類實現單例
public class Singleton{ private static class SingletonHolder{ private static Singleton instance = new Singleton(); } private Singleton(){}; private static volatile Singleton instance = null; public static Singleton getInstance(){ return SingletonHolder.instance; }}
Singleton的執行個體被靜態內部類SingletonHolder持有,只有在調用getInstance方法時,SingletonHolder才會被載入,instance被執行個體化。既實現懶載入,也不會有安全執行緒問題。
java單例類的幾種實現