MO_or的單例模式複習總結

1、引言

本篇文章是源於工做中遇到了一個與單例模式相關的問題。並由此對單例模式做一個複習總結。

2、背景

在一次開發過程當中,控制檯拋出了一個關於寫入數據庫時,主鍵衝突的異常。最後排查問題後主要是由於如下三點:
1)獲取數據庫鏈接的方式並非單例的;
2)加載頁面時同時請求了相同的接口兩次;
3)主鍵採用的是自動增加。
固然這裏如果須要快速解決bug,只需解決問題2,既加載頁面時只請求一次該接口。
可是問題1纔是潛藏更深的隱患。由於較複雜的業務將會佔用過多的多餘鏈接,影響系統性能,並可能再次致使上述問題。

3、總結

一、什麼是單例模式?

在瞭解單例模式以前,咱們應該先要明白兩個問題,什麼叫設計模式,設計模式與單例模式又是怎樣的關係。

設計模式是面向對象的軟件開發員,經過漫長的試驗和錯誤總結出來的一套解決通常問題的方案。
而單例模式,就是其中一種針對一類問題的解決方案。

這就比如設計模式是獨孤九劍,而單例模式和其它不一樣的設計模式就比如破劍式、破刀式。

破劍式就是破解普天下各門各派劍法的招式。那麼單例模式又是解決什麼問題的呢?而且是如何解決的呢?

單例模式提供了一種最佳的建立對象的方式:
一個單一的類,負責建立本身的對象,同時確保只建立一個對象,而且提供了惟一一個訪問此對象的方法,且不須要再實例化該對象。

二、使用場景

如今已經明白單例模式使用的要點既,一個類僅一個實例,而且提供訪問對象惟一全局方法。

那麼單例模式主要解決的是什麼呢?
一個全局的類頻繁的建立和消耗。

如何解決的呢?
判斷是否已有其單例,有則返回,沒有則建立。

什麼時候使用呢?
當咱們想控制實例化數量,節省系統資源時。

現實場景:
1)計劃生育,一對夫妻只有一個孩子;
2)windows操做一個文件時,屢次打開,始終只有這一個文件。

抽象到程序中的使用場景:
1)生成惟一序列號;
2)頁面上的計數器;
3)建立對象須要消耗比較多的資源,好比IO和數據庫的鏈接。

三、實現方式

這裏並不會詳細記錄其實現步驟(詳細步驟能夠參考第四大點中的連接),此處僅記錄實現單例模式的幾種具體方法與各自優劣。

3.一、懶漢式(線程不安全)html

示例:

public class Single4Lazy {

    private static Single4Lazy instance;

    private Single4Lazy() {
    }

    public static Single4Lazy getInstance() {

        if(instance == null) {
            instance = new Single4Lazy();
        }

        return instance;
    }
}

優勢:實現簡單,懶加載。

缺點:線程不安全(沒有加鎖),嚴格來講並不能算單例模式。

3.二、懶漢式(線程安全)數據庫

示例:

public class Single4LazySynchronized {

    private static Single4LazySynchronized instance;

    private Single4LazySynchronized() {
    }

    public static synchronized Single4LazySynchronized getInstance() {

        if (instance == null) {
            instance = new Single4LazySynchronized();
        }

        return instance;
    }
}

優勢:實現簡單,線程安全(須要加鎖),懶加載。

缺點:效率低(getInstance()方法不能頻繁使用,不然影響效率)。

3.三、餓漢式windows

示例:

public class SingleObject {

    /**
     * 建立一個私有的單例對象
     */
    private static SingleObject instance = new SingleObject();

    /**
     * 構造方法設爲私有,這樣就不會實例化對象
     */
    private SingleObject() {
    }

    /**
     * 獲取惟一可用對象
     */
    public static SingleObject getInstance(){
        return instance;
    }

    public void showMessage() {
        System.out.println("Hello, World ! ");
    }
    
優勢:線程安全,實現簡單,效率較高(不用加鎖)。

缺點:非懶加載,容易生成垃圾對象。

3.四、雙檢鎖(DCL, double-checked locking)設計模式

示例:

public class Single4DCL {

    private static Single4DCL instance;

    private Single4DCL() {
    }

    public static Single4DCL getInstance() {

        if (instance == null) {

            /**
             * getInstance()是靜態方法,因此不能使用未靜態或未實例的類對象
             */
            synchronized (Single4DCL.class) {

                if (instance == null) {
                    instance = new Single4DCL();
                }
            }
        }

        return instance;
    }
}

優勢:線程安全,懶加載,效率較高。

缺點:JDK1.5起,實現較複雜,getInstance()性能對應用程序很關鍵。

3.五、靜態內部類安全

示例:

public class Single4Static {

    private static class Single4StaticHolder {
        private static final Single4Static INSTANCE = new Single4Static();
    }

    private Single4Static() {
    }

    public static Single4Static getInstance() {
        return Single4StaticHolder.INSTANCE;
    }

}

優勢:線程安全,效率較高,懶加載,實現難度通常。

缺點:只適用於靜態域狀況。

3.六、枚舉性能

示例:

public enum Single4Enum {

    INSTANCE;

    public void whateverMethod() {

    }
    
}

優勢:線程安全,最佳實現,自動支持序列化機制,絕對防止屢次實例化,不能經過reflection attack來調用私有構造方法

缺點:JDK1.5起

四、小tips

構造器是私有的。
一般不使用第一、2種,通常使用第3種,只有明確須要懶加載時使用第5種,
須要反序列化建立時能夠嘗試第6種,有其它特殊需求時,能夠考慮第4種。

4、參考

https://www.runoob.com/design...線程

https://www.runoob.com/design...設計

https://baike.baidu.com/item/...code

5、最後

如有不足,歡迎指正。
求知若渴,虛心若愚。
相關文章
相關標籤/搜索