[java設計模式]單例設計模式,程式也要進行計劃生育。

小杆子發表於2021-01-23

記憶體媽媽太累了,再也撐不起太多的物件了。
單例設計模式

何為單例設計模式:
顧名思義,單:表示一個;:表示例項;單例:表示一個例項;也就是說我們的程式在執行的整個生命週期中,只建立一個物件,而不去建立多個物件。理解起來很簡單吧。

何種場景只需要一個物件?
保證第一點,沒有多個執行緒度這個物件的變數進行寫操作,只要滿足這一點,基本上都可以使用單例設計模式。比如:配置類,Spring中配置的Controller、Service、dao層的物件。

單例設計模式的特徵:
不管你程式碼怎麼寫,只要滿足以下特徵即可:

  • 構造方法私有化。禁止外部建立物件。
  • 提供一個外部訪問的介面。

常見的單例設計模式:

  1. 懶漢式
  2. 餓漢式
  3. 雙重檢測鎖
  4. 靜態內部類
  5. 列舉
    常見的這五種單例設計模式,也無非就是利用JVM的特性,對何時進行載入,做了簡單的區分。是程式啟動的時候載入?還是類初始化的時候載入?

程式碼具體實現如下:

懶漢式

簡單理解就是不讓jvm啟動的時候對單例進行載入,好處就是節約記憶體。可不嘛,不載入那麼多物件,當然節省記憶體了。

/** 
 * @ClassName : lazybones 懶漢單例
 * @Description :   懶漢設計模式,載入的時候不進行初始化。
 */
public class Lazybones { 
    private static Lazybones instance; 

    /** * 私有構造方法,防止外部建立物件 */
    private Lazybones() {
    } 

    /** * 外部過去例項的通道。
     *
     * @return
     */
    public static synchronized Lazybones getInstance() { 
    if (instance == null) {
            instance = new Lazybones();
        } return instance;
    }
}

餓漢式

簡單理解就是JVM啟動的時候就進行載入這個單例物件。好處就是再使用的時候,直接獲取物件進行呼叫即可。因為類只會載入一次,所以物件也只會被建立一次。

/**
 * @ClassName : Eager  //餓漢單例
 * @Description :   //類載入的時候,進行初始化。
 */
public class Hungry {
    private static Hungry instance = new Hungry();

    /**
     * 私有構造方法
     */
    private Hungry() {
    }

    /**
     * 外部過去例項的通道
     * @return
     */
    public static Hungry getInstance() {
        return instance;
    }
}

雙重檢測鎖

其實就是針對懶漢模式的一種優化,我們直接在方法上加鎖,導致每次獲取這個物件的時候,都需要先獲取鎖,效率太低了。 但是雙重檢測鎖有一個坑,後續慢慢的講。

/**
 * @ClassName : DoubleDetectionLock  //雙重檢測鎖
 * @Description : 雙重檢測鎖,優化了懶漢單例模式的效能問題,通過加volatile關鍵是防止出現指令重排序。
 */
public class DoubleDetectionLock {
    // volatile關鍵字可以防止指令重排序,避免產生bug
    private static volatile DoubleDetectionLock instance;

    /**
     * 私有構造方法
     */
    private DoubleDetectionLock() {
    }

    /**
     * 外部獲取渠道。
     * @return
     */
    public static DoubleDetectionLock getInstance() {
        if (instance == null) {
            synchronized (DoubleDetectionLock.class) {
                if (instance == null){
                    instance = new DoubleDetectionLock();
                }
            }
        }
        return instance;
    }
}

靜態內部類

此方式也滿足懶載入,因為即使內部類只有在自己被呼叫的時候才會進行初始化。而且兼顧使用到了類只被載入一次的特性。

/**
 * @ClassName : InnerClass 內部靜態類單例
 * @Description : 內部靜態類只有被去呼叫的時候,才進行初始化。
 */
public class InnerClass {

    /**
     * 私有構造方法
     */
    private InnerClass(){
    }

    /**
     * 內部類初始化的時候,建立。
     */
    private static class Instance{
        private static InnerClass instance = new InnerClass();

    }

    /**
     * 外部獲取的通道
     * @return
     */
    public static final InnerClass getInstance(){
        return Instance.instance;
    }
}

列舉類

天然的單例設計模式,這個平常用的不太多

/**
 * 列舉也是天然的單例模式
 */
public enum EnumSingleton {
    INSTANCE;
}

結尾

單例設計模式差不多,到此為止了。下一篇講講怎麼去破解單例設計模式,以及怎麼去防護。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章