【多線程】多線程下的七種單例模式

一、餓漢式

此方式的關鍵在於instance作爲類變量並且直接得到了初始化

public class SingletonObject1 {
    private static final SingletonObject1 instance=new SingletonObject1();
    public static SingletonObject1 getInstance(){
        return instance;
    }
}

缺點: 不能懶加載(佔用內存)

二、懶漢式

懶漢式就是使用類實例的時候再去創建,避免出現類在初始化的時候提前創建

public class SingletonObject2 {
    // 定義實例,但不進行初始化
    private static SingletonObject2 intance;
    public static SingletonObject2 getInstance(){
        if (null==intance){
            intance= new SingletonObject2();
        }
        return SingletonObject2.intance;
    }
}

缺點: 懶漢式可以保證實例的懶加載,但是無法保證實例的唯一性。

三、懶漢式+同步方法

在多線程的情況下,多個線程同時訪問instance(共享資源)需要保證數據的一致性,增加同步的約束。

public class SingletonObject3 {
    // 定義實例,但不進行初始化
    private static SingletonObject3 intance;
    // 向getInstance方法加入同步控制,每次只能有一個線程能夠進入
    public synchronized static SingletonObject3 getInstance(){
        if (null==intance){
            intance= new SingletonObject3();
        }
        return SingletonObject3.intance;
    }
}

缺點:儘管synchornized已經進行了優化,但是此關鍵字依舊導致getInstance方法只能在同一時刻被一個線程訪問,降低了性能。

四、Double-Check

此種方式提供了一種高效的數據同步策略,就是首次初始化時加鎖。

public class SingletonObject6 {
    // 定義實例,但不進行初始化
    private static SingletonObject6 instance;
    
    public static SingletonObject6 getInstance(){
        // 當instance爲null的時候,進入代碼塊,同時判讀那避免每次都需要進入同步代碼塊,可以提高效率
        if (null==instance){
            synchronized (SingletonObject6.class){
                // 判斷instance是否被創建
                if(null==instance){
                    instance=new SingletonObject6();
                }
            }
        }
        return SingletonObject6.instance;
    }
}

缺點:在多線程的情況下可能會引起空指針異常

五、volatile+Double-Check

加入volatile關鍵字防止jvm在運行時指令重排序

public class SingletonObject5 {
   // 定義實例,但不進行初始化,加volatile使其對象可見
    private volatile static SingletonObject5 instance;
   
    public static SingletonObject5 getInstance(){
        // 當instance爲null的時候,進入代碼塊,同時判讀那避免每次都需要進入同步代碼塊,可以提高效率
        if (null==instance){
            synchronized (SingletonObject5.class){
                // 判斷instance是否被創建
                if(null==instance){
                    instance=new SingletonObject5();
                }
            }
        }
        return SingletonObject5.instance;
    }
}

六、靜態內部類的方式

藉助類加載的特點,對單例進行重構。在Singleton類中沒有instance的靜態成員,而是將其放到了靜態內部類中,以保證在Singleton類初始化過程中不會創建Singleton的實例,在靜態內部類中定了singleton的靜態變量,並且直接進行了初始化。

public class SingletonObject3 {
    // 在靜態內部類中喫有Singleton的實例,並且可以被直接初始化
    private static class InstanceHolder{
        private static SingletonObject3 instance=new SingletonObject3();
    }
    // 調用getInstance方法,事實上是獲得靜態內部類的instance靜態屬性
    public static SingletonObject3 getInstance(){
        return InstanceHolder.instance;
    }
 }

七、枚舉的方式

推薦使用的一種方式,枚舉類型不允許被繼承,同樣是線程安全的且只能被實例化一次,但是枚舉類型不能進行懶加載,對Singleton主動使用。

public class SingletonObject4 {
    // 枚舉本身就是final的,不允許被繼承
    private enum EnumSingleton{
        // 實例變量
        INSTSNCE;
        private SingletonObject4 instance;

        EnumSingleton(){
            this.instance=new SingletonObject4();
        }

        public SingletonObject4 getInstance(){
            return instance;
        }
    }
    public static SingletonObject4 getInstance(){
        return EnumSingleton.INSTSNCE.getInstance();
    }
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章