單例模式:5種實現方式

碼農StayUp發表於2021-02-15

微信搜尋:碼農StayUp
主頁地址:https://gozhuyinglong.github.io
原始碼分享:https://github.com/gozhuyinglong/blog-demos

1. 單例模式

單例模式(Singleton Pattern)是一種簡單的物件建立型模式。該模式保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

所以要實現單例模式,要做到以下幾點:

  • 將構造方法私有化,杜絕使用構造器建立例項。
  • 需要自身建立唯一的一個例項,並提供一個全域性訪問入口

2. 單例模式的幾種實現

對於單例模式有以下5種實現。

2.1. 懶漢式

該方式是使用synchronized關鍵字進行加鎖,保證了執行緒安全性。
優點:在第一次呼叫才初始化,避免了記憶體浪費。
缺點:對獲取例項方法加鎖,大大降低了併發效率。

由於加了鎖,對效能影響較大,不推薦使用。

public class SingletonLazy {

    /**
     * 私有例項
     */
    private static SingletonLazy instance;

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

    /**
     * 唯一公開獲取例項的方法(靜態工廠方法),該方法使用synchronized加鎖,來保證執行緒安全性
     *
     * @return
     */
    public static synchronized SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }

}

2.2 餓漢式

餓漢式是利用類載入機制來避免了多執行緒的同步問題,所以是執行緒安全的。
優點:未加鎖,執行效率高。
缺點:類載入時就初始化例項,造成記憶體浪費。

如果對記憶體要求不高的情況,還是比較推薦使用這種方式。

public class SingletonEager {

    /**
     * 私有例項,靜態變數會在類載入的時候初始化,是執行緒安全的
     */
    private static final SingletonEager instance = new SingletonEager();

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

    /**
     * 唯一公開獲取例項的方法(靜態工廠方法)
     *
     * @return
     */
    public static SingletonEager getInstance() {
        return instance;
    }
}

2.3 雙重校驗鎖

利用了volatile修飾符的執行緒可見性(被一個執行緒修改後,其他執行緒立即可見),即保證了懶載入,又保證了高效能,所以推薦使用。

public class SingletonDCL {

    /**
     * 私有例項,volatile修飾的變數是具有可見性的(即被一個執行緒修改後,其他執行緒立即可見)
     */
    private volatile static SingletonDCL instance;

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

    /**
     * 唯一公開獲取例項的方法(靜態工廠方法)
     *
     * @return
     */
    public static SingletonDCL getInstance() {
        if (instance == null) {
            synchronized (SingletonDCL.class) {
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

2.4 靜態內部類

該模式利用了靜態內部類延遲初始化的特性,來達到與雙重校驗鎖方式一樣的功能。由於需要藉助輔助類,並不常用。

public class SingletonInnerClass {

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

    /**
     * 唯一公開獲取例項的方法(靜態工廠方法)
     *
     * @return
     */
    public static SingletonInnerClass getInstance() {
        return LazyHolder.INSTANCE;
    }

    /**
     * 私有靜態內部類
     */
    private static class LazyHolder {
        private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
    }
}

2.5 列舉類

該方式利用了列舉類的特性,不僅能避免執行緒同步問題,還防止反序列化重新建立新的物件。這種方式是 Effective Java 作者 Josh Bloch 提倡的方式。

但由於這種編碼方式還不能適應,所以實際工作中很少使用。

public enum SingletonEnum {

    INSTANCE;

    public void method() {
        System.out.println("列舉類中定義方法!");
    }

}

推薦閱讀

相關文章