1. 程式人生 > >java單例類的幾種實現

java單例類的幾種實現

nta span 安全 let 實例化 註意 style 獲取 單例類

一,最簡單的方式

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單例類的幾種實現