Java單例模式的七種實現


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;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM