1. 程式人生 > >設計模式---單例設計模式

設計模式---單例設計模式

單例模式的定義:確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項。

說白了就是,保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

從定義中,我們可以看出:

  1. 單例類只能有一個例項。
  2. 單例類必須自行建立自己的唯一的例項。
  3. 單例類必須給所有其他物件提供這一例項 ,向整個系統提供這個例項。

餓漢單例:

/**
 * 餓漢
 */
public class HungrySingleton {

    // 顧名思義:餓漢就是一開始就直接例項化
    private static HungrySingleton sInstance = new HungrySingleton()
; //私有化空構造方法 // 不允許外部例項化 private HungrySingleton() { // TODO: init something,if needed! } //靜態方法返回單例類物件 public static HungrySingleton getInstance() { return sInstance; } }

餓漢模式是執行緒安全的,因為類載入時就會初始化單例物件,並且只初始化一次在這裡插入圖片描述 餓漢式單例雖然在首次呼叫的時候快,但是類載入時就會初始化單例物件,容易產生垃圾。 可以與java.lang.RunTime對照著看: 在這裡插入圖片描述

懶漢模式:

/**
 * 懶漢:首次呼叫的時候才會進行例項化
 */
public class LazySingleton {

    private static LazySingleton sInstance;

    //私有化空構造方法
    private LazySingleton() {
    }

    //靜態方法返回單例類物件
    public static LazySingleton getInstance() {
        //懶載入
        if (sInstance == null) {
            sInstance = new LazySingleton
(); } return sInstance; } }

PS: 執行緒不安全,因為getInstance()方法沒有做任何的同步處理。 因為是首次呼叫的時候才會例項化,所以相比餓漢式單例會比較節省資源。 由於執行緒不安全,所以我們可能就會想著加鎖,可是加鎖後面臨的問題就是每次外部呼叫getInstance()時效率會明顯下降。 像下邊這樣:

// 靜態方法屬於類,給靜態方法加鎖相當於把整個類都鎖住了
    public synchronized static LazySingleton getInstance() {
        //懶載入
        if (sInstance == null) {
            sInstance = new LazySingleton();
        }
        return sInstance;
    }

這就成執行緒安全的懶漢模式了。(一般不會這麼做的)

雙檢鎖(DCL:Double Check Lock):

/**
 * @author yuan
 * @version 1.0.0
 * @Description DCL
 * @date 2018/10/4 17:11
 */

public class DCLSingleTon {
    private static DCLSingleTon sInstance;

    private DCLSingleTon() {
    }

    public static DCLSingleTon getInstance() {
        // first check
        if (null == sInstance) {
            synchronized (DCLSingleTon.class) {
                // second check
                if (null == sInstance) {
                    sInstance = new DCLSingleTon();
                }
            }
        }
        return sInstance;
    }

}

DCL:執行緒安全。相比加鎖後的懶漢式單例更加有效率。 剛開始我還在想:為什麼要判空兩次呢? 第一次判空後,如果為null就已經加鎖了呀!後來想了想確實兩次判空是有道理的,假如有這種情況: 執行緒A和執行緒B同時進行了第一次判空,都是null那麼自然就順利進入程式碼塊中,然而此時由於加鎖原因執行緒排隊阻塞,等待其中一個執行緒執行完後,另外一個進入synchronized塊中執行,此時如果沒有第二次判空,那麼就會重複例項化,執行緒就不安全了。而二次判空就可以完美解決這個問題。

靜態內部類單例:

/**
 * @author yuan
 * @version 1.0.0
 * @Description 靜態內部類單例模式
 * @date 2018/10/4 17:30
 */

public class StaticInnerClassSingleton {
    private static StaticInnerClassSingleton sInstance;

    public static StaticInnerClassSingleton getInstance() {
        // 靜態內部類只有在外部有人使用的時候才會進行載入,並不是和外部類一起載入的。(懶載入)
        return SingleClass.STATICINNERCLASSSINGLETON;
    }

    //靜態內部類
    private static class SingleClass {
        private static final StaticInnerClassSingleton STATICINNERCLASSSINGLETON = new StaticInnerClassSingleton();
    }

}

靜態內部類只有在外部有人使用的時候才會進行載入,並不是和外部類一起載入的。(懶載入) 靜態內部類中的靜態變數是通過類載入器初始化的,所以執行緒安全。