天天看點

一天一個設計模式:單例模式概念:特點:餓漢式單例類懶漢式單例雙重檢查加鎖懶加載模式(内部類形式)單例類枚舉:

概念:

作為對象的建立模式,單例模式確定某一個類隻有一個執行個體,而且自行執行個體化,并向整個系統提供這個執行個體。

特點:

  1.單例類隻能有一個執行個體

  2.單例類必須建立自己的唯一執行個體

  3.單例類必須給其他所有對象提供這一執行個體。

餓漢式單例類

public class EagerSingleton {
    private static EagerSingleton instance = new EagerSingleton();
    /**
     * 私有預設構造子
     */
    private EagerSingleton(){}
    /**
     * 靜态工廠方法
     */
    public static EagerSingleton getInstance(){
        return instance;
    }
}           

複制

由于選用的是靜态資源,在類加載的時候,靜态變量instance就會被初始化,類的唯一變量也在這時候建立了出來。

餓漢式,顧名思義,急于建立對象,在類加載的時候就完成了對象的建立。

餓漢式是一種采用“空間換取時間”,當類裝載的時候就會建立類的執行個體,不管你用不用,先建立,調用的時候,無需判斷直接使用,節約了時間。

懶漢式單例

public class LazySingleton {
    private static LazySingleton instance = null;
    /**
     * 私有預設構造子
     */
    private LazySingleton(){}
    /**
     * 靜态工廠方法
     */
    public static synchronized LazySingleton getInstance(){
        if(instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }
}           

複制

隻有在真正需要的時候才會去建立,采用的是時間換空間,由于是線程安全的,會降低通路速度。固可使用雙檢鎖的方式進行優化。

雙重檢查加鎖

機制:并不是每次進入getInstance方法都需要同步,而不是先同步,進入方法後,先檢查執行個體是否存在,如果不存在才進行下面的同步塊,這是第一檢查,進入同步塊後,在檢查執行個體是否存在,如果不存在,就在同步的情況下建立一個執行個體,這是第二重檢查,這樣一來就隻需要同步一次,減少了多次在同步情況下進行判斷所浪費的時間。

雙檢鎖的關鍵是,使用volatile,它在此處的作用是,該變量将不會被某一個線程緩存,而是在共享記憶體中,被所有進行讀寫的記憶體共享到,進而保證多個線程能夠正确處理該變量。

public class Singleton {
    private volatile static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance(){
        //先檢查執行個體是否存在,如果不存在才進入下面的同步塊
        if(instance == null){
            //同步塊,線程安全的建立執行個體
            synchronized (Singleton.class) {
                //再次檢查執行個體是否存在,如果不存在才真正的建立執行個體
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}           

複制

注:由于volatile關鍵字會屏蔽虛拟機的一些代碼優化,是以運作效率并不高,所有其實應該盡量避免使用雙檢鎖的方式來實作單例。

懶加載模式(内部類形式)

什麼是内部類:

  内部類就是指在類裡面的類,被static修飾的内部類,稱為類級内部類,無static修飾的内部類稱為對象級内部類。

  類級内部類是外部類的static部分,它的對象與外部類對象間并沒有依賴關系,是以可以直接建立。而對象級内部類的執行個體,是綁定在外部對象的執行個體中的。

  類級内部類中,可以定義靜态方法,在靜态方法中隻能夠引用外部類中的靜态成員或者成員變量。

  類級内部類相當于外部類的成員,隻有在第一次被使用的時候才會加載。

多線程預設同步鎖的相關知識:

  在下面操作下,jvm會自動為該操作進行同步,以避免出現并發安全問題。

  1.由靜态初始化器(在靜态字段上或static{}塊中的初始化器)初始化資料。

  2.通路final字段時,

  3.在建立線程前建立對象時,

  4.線程可以看見它将要處理的對象時。

綜上的解決思路:

  采用靜态初始化器的方式通過jvm來保證線程的安全性,采用類級内部類的方式實作延遲加載。

public class Singleton {
    
    private Singleton(){}
    /**
     *    類級的内部類,也就是靜态的成員式内部類,該内部類的執行個體與外部類的執行個體
     *    沒有綁定關系,而且隻有被調用到時才會裝載,進而實作了延遲加載。
     */
    private static class SingletonHolder{
        /**
         * 靜态初始化器,由JVM來保證線程安全
         */
        private static Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}           

複制

  在getInstance第一次被調用時,會讀取内部類的instance,這時,類級内部類完成初始化,進而建立單例,實作了延時加載,由于是靜态成員,隻有在類加載的時候才加載一次,且通過jvm的預設方式實作了線程安全問題。

單例類枚舉:

  單元素的枚舉類型已經成為實作Singleton的最佳方法。用枚舉來實作單例非常簡單,隻需要編寫一個包含單個元素的枚舉類型即可。

public enum Singleton {
    /**
     * 定義一個枚舉的元素,它就代表了Singleton的一個執行個體。
     */
    
    uniqueInstance;
    
    /**
     * 單例可以有自己的操作
     */
    public void singletonOperation(){
        //功能處理
    }
}           

複制

  使用枚舉來實作單執行個體控制會更加簡潔,而且無償地提供了序列化機制,并由JVM從根本上提供保障,絕對防止多次執行個體化,是更簡潔、高效、安全的實作單例的方式。