1.餓漢式
//final不允許類被繼承 public final class Singleton { //實例化變量 private byte[] data=new byte[1024]; //直接初始化 private static Singleton instance=new Singleton(); //私有構造函數,不允許外部new private Singleton(){ } public static Singleton getInstance(){ return instance; } }
如果主動使用Singleton類,instance實例將直接完成創建,包括其中的實例變量都會得到初始化,但是instance可能被加載很長一段時間后才被使用,instance實例開辟的堆內存會駐留更久的時間,如果說一個類的成員變量不多,且占用內存資源較少,可以使用餓漢式,總結它可以保證多個線程下唯一實例,getInstance方法性能較高,但是無法進行懶加載。
2.懶漢式
//final不允許類被繼承 public final class Singleton { //實例化變量 private byte[] data=new byte[1024]; private static Singleton instance=null; //私有構造函數,不允許外部new private Singleton(){ } //等到需要使用時進行創建 public static Singleton getInstance(){ if (instance==null){ instance=new Singleton(); } return instance; } }
該方法在多線程環境下不能保證單例的唯一性。
3.懶漢+同步方法
//final不允許類被繼承 public final class Singleton { //實例化變量 private byte[] data=new byte[1024]; private static Singleton instance=null; //私有構造函數,不允許外部new private Singleton(){ } //等到需要使用時進行創建 public static synchronized Singleton getInstance(){ if (instance==null){ instance=new Singleton(); } return instance; } }
可以保證在多線程環境下單例的唯一性,但是synchronied關鍵字會導致在同一時刻方法只能被一個線程所訪問,性能低下。
4.雙重鎖檢查
//final不允許類被繼承 public final class Singleton { //實例化變量 private byte[] data=new byte[1024]; private static Singleton instance=null; //私有構造函數,不允許外部new private Singleton(){ } //等到需要使用時進行創建 public static Singleton getInstance(){ if (instance==null){ synchronized (Singleton.class){ if (instance==null) instance=new Singleton(); } } return instance; } }
這段代碼看起來很完美,很可惜,它是有問題。主要在於instance = new Singleton()這句,這並非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情:
1.給 instance 分配內存
2.調用 Singleton 的構造函數來初始化成員變量
3.將instance對象指向分配的內存空間(執行完這步 instance 就為非 null 了)
但是在 JVM 的即時編譯器中存在指令重排序的優化。也就是說上面的第二步和第三步的順序是不能保證的,最終的執行順序可能是 1-2-3 也可能是 1-3-2。如果是后者,則在 3 執行完畢、2 未執行之前,被線程二搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯。
5.volatile+雙重鎖檢查
//final不允許類被繼承 public final class Singleton { //實例化變量 private byte[] data=new byte[1024]; //禁止指令重排序 private volatile static Singleton instance=null; //私有構造函數,不允許外部new private Singleton(){ } //等到需要使用時進行創建 public static Singleton getInstance(){ if (instance==null){ synchronized (Singleton.class){ if (instance==null) instance=new Singleton(); } } return instance; } }
加上volatile禁止了指令的重排許操作。滿足多線程下的單例,懶加載,獲取實例的高效性。
6.Holder方式
//final不允許類被繼承 public final class Singleton { //實例化變量 private byte[] data=new byte[1024]; //私有構造函數,不允許外部new private Singleton(){ } //在靜態內部類中持有singleton的實例,可以被直接初始化 private static class Holder{ private static Singleton instance=new Singleton(); } public static Singleton getInstance(){ return Holder.instance; } }
Holder類中定義了Singleton的靜態變量,並且直接進行了實例化,當Holder被主動引用的時候會創建Singleton實例。Holder方式的單例模式是最好的設計之一,也是目前用的比較廣泛的設計之一。
7.枚舉方式
枚舉類型不允許被繼承,同樣是線程安全的且只能被實例化一次,但是枚舉類型不能夠懶加載,對Singleton主動使用,比如調用其中的靜態方法則INSTANCE會立即得到實例化。
public enum Singleton{ INSTANCE; private byte[] data=new byte[1024]; Singleton(){ System.out.println("INSTANCE will be initialized immediately"); } public static void method(){ //調用該方法會主動使用Singleton,INSTANCE實例將會被實例化 } public static Singleton getInstance(){ return INSTANCE; } }