請手寫一個Singleton

1. 手寫一個Singleton

1.1. 餓漢式

  • 特點:
    在調用方法前,實例已經被創建了。

  • java實現

    class Singleton{
     //缺點:不能有其他實例變量,因爲getInstance方法沒有同步所以會出現非線程安全問題。
       private static Singleton instance = new Singleton();
       private Singleton(){}
       public static Singleton getInstance(){
           return instance;
       }
   }
  • c實現

後續補充…

  • c++實現
    Singleton *Singleton::instance = new Singleton();

    Singleton* getInstance()
    { 
        return instance;
    }
  • python實現

    後續補充…

1.2. 懶漢式

  • 特點:
    延遲加載就是在調用get()方法時實例才被創建。
  • java實現
  class Singleton{
      private static Singleton instance = null;
      private Singleton(){}
      //懶漢式必須要加上synchronize同步鎖關鍵字,否則線程不安全。
      //缺點是效率低,public static synchronized 全部代碼加上了鎖等同於synchronized(Singleton.class)
      public static synchronized Singleton getInstance(){
          if(instance == null){
              instance = new Singleton();
          }
          return instance;
      }
  }
  • c實現

    後續補充…

  • c++實現

    Singleton* getInstance()
    {
        lock();
        if (instance == NULL)
        {
        instance = new Singleton();
        }
        unlock();

        return instance;
    }
  • python實現

    後續補充…

1.3. 雙重校驗鎖(改造版的懶漢式)

  • 特點:

    1. 進行兩次null檢查提升了併發度,因爲在單例中new的情況非常少,絕大多數都是可以並行的讀操作。因此在加鎖前多進行一次null檢查就可以減少絕大多數的加鎖操作,執行效率提高的目的也就達到了。
    2. volatile關鍵字保證了各線程對singleton靜態實例域修改的可見性
    3. volatile禁止指令重排序優化。提高了多線程情況下的安全性
  • java實現
   public class Singleton {
        private volatile static Singleton singleton;
        private Singleton() {}
        public static Singleton getSingleton() {
            if (singleton == null) {
                //此處加鎖
                synchronized (Singleton.class) {
                    if (singleton == null) {
                            singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

  • c++實現
    Singleton* getInstance()
    {
        if (instance == NULL)
        {
        lock();
            if (instance == NULL)
            {
                instance = new Singleton();
            }
            unlock();
        }

        return instance;
    }

1.4. 靜態內部類實現單例(線程安全、效率高)

  • 特點:
    1. 同時具備線程安全和效率高特點
    2. 這種方式下 Singleton 類被裝載了,instance 不一定被初始化。因爲 SingletonHolder 類沒有被主動使用,只有通過顯式調用 getInstance 方法時,纔會顯式裝載 SingletonHolder 類,從而實例化 instance。
  • java實現
    public class Singleton {  
        private static class SingletonHolder {  
        //注意內部類SingletonHolder要用static修飾且其中的靜態變量INSTANCE必須是final的    
        private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
        }  
    }

以上四種都有兩個問題:

1. 都需要額外的工作(Serializable、transient、readResolve())來實現序列化,否則每次反序列化一個序列化的對象實例時都會創建一個新的實例。
2. 可能會有人使用反射強行調用我們的私有構造器(如果要避免這種情況,可以修改構造器,讓它在創建第二個實例的時候拋異常)。

解決方法:
實現 Serializable接口。並重寫readResolve()方法

@override
protected Object readResolve() throws ObjectStreamException{
   return SingletonHolder.INSTANCE; 
}

1.4. 枚舉寫法實現單利(Effective Java推薦)

  • 特點:

    使用枚舉除了線程安全和防止反射強行調用構造器之外,還提供了自動序列化機制,防止反序列化的時候創建新的對象

  • java實現
// 定義單例模式中需要完成的代碼邏輯
public interface MySingleton {
    void doSomething();
}

public enum Singleton implements MySingleton {
    INSTANCE {
        @Override
        public void doSomething() {
            System.out.println("complete singleton");
        }
    };

    public static MySingleton getInstance() {
        return Singleton.INSTANCE;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章