# Singleton ###### tags: `Design Patten` `Singleton` 使用時機 : 在某些情況下,只能使用同一實例來達到目的,否則造成資料上的問題 注意 : 多執行序、反射、序列化 1. **餓漢模式** instance 為靜態(類)變量,在初始化階段過程中會被收集進< clinit >()方法中,該方法保證同步,因此instance在多執行緒環境不可能被實例化兩次。 缺點是生命週期很長 輕量級物件比較適合 public final class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){}; private static Singleton getInstance() return instance; } 2. **懶漢模式** 與餓漢模式的差別為主動取得時才實例化 public final class Singleton{ private static Singleton instance = null; private Singleton(){}; private static Singleton getInstance() { if(instance == null){ instance = new Singleton(); } return instance; } } 3. **懶漢+同步模式** 使用排他鎖進行同步,缺點效能不好 public final class Singleton{ private static Singleton instance = null; private Singleton(){}; private static synchronized Singleton getInstance() { if(instance == null){ instance = new Singleton(); } return instance; } } 4. **Double Check** 滿足了懶加載、instance唯一性,但在多執行緒環境下"可能"會有問題。 在建構Singleton時需要實例化con或者更多其他實例以及自己,根據JVM運行時指令重排序及happen-before規則,他們並沒有前後倚賴的關係,因此當重排序後假如instance最先完成,con未完成實例,而其他執行緒取得的instance的實例並且執行操作可能會導致NullPointerException。 public final class Singleton{ private static Singleton instance = null; Connection con ; private Singleton(){ this.con....... }; private static Singleton getInstance() { if(instance == null){ synchronized(Singleton.class){ if(instance == null ){ instance = new Singleton(); } } } return instance; } } 5. Volatitle + Double Check 解決4.的方法則是加入關鍵字volatile private volatile static Singleton instance = null; 此關鍵字可防止指令重排序確保instance在最後才實例化 6. **Holder** 此模式借助了ClassLoader的特點,透過靜態內部類維護Singleton Object 的 Instance。 當內部類被主動引用時,會建立Singleton Object的Instance ,且創見過程在Java 編譯時期會被收集至< clinit >()內 ,並且此方法為同步分法,可保證原子性、有序性、可見性,是目前單利設計中最好的設計之一。但使用時必須考慮到反射及反序列化可能帶來的風險或攻擊。 public final class Singleton{ private Singleton(){ //防止反射 if (Holder.instance != null) throw new IllegalStateException(); } private static class Holder{ private static Singleton instance = new Singleton(); } public static Singleton getInstance(){ return Holder.instance; } } 7. **列舉模式** 該類型不允許被繼承、線程安全且只能被實例化一次、無法懶加載(需透過間接方式達成懶加載 EX : 內部列舉 ) 列舉的序列化及反序列化交由JVM處理,並且不可使用writeReplace、readResolve等方法 public enum Singleton{ INSTANCE; Singleton(){} public static Singleton getInstance(){ return INSTANCE; } }