設計模式之單例模式

        寫作緣由:對於一個有逼格的程序猿,23種設計模式應該不在話下,爲了擠身逼格程序猿之列,決定系統的學習設計模式

        關於設計模式(主要是思想方面)

               001.定義:

                               是一套反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結

               002.目的:

                               爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性


        設計模式之最簡單模式 ------> 單例模式:

               001.定義:

                               確保單例類只有一個實例,並且這個單例類提供一個函數接口讓其他類獲取到這個唯一的實例

               002.生活情景:

                              古代皇帝,有且只能有一個,多個則會出問題...

                              男人娶老婆,有且只能有一個,多了則會...

               003.編程情景:

                              如果某個類,創建時需要消耗很多資源,即new出這個類的代價很大;或者是這個類佔用很多內存,如果創建太多這個類實例會導致內存佔用太多,簡單點比如:

                              配置文件、工具類、線程池、緩存、日誌等等,這些需要保證整個應用中有且只有一個實例

               004.分類:餓漢式、懶漢式(代碼已經做了詳細說明) 

 

public class Singleton {
    //-----------------------------餓漢模式----------------------------
    /**
     * 餓漢式的特點是加載類的時候比較慢,要創建類的唯一的實例,但是運行時獲取
     * 對象的速度比較快
     * 線程安全
     */
    //1.將構造方法私有化,不允許外部直接創建對象
    private Singleton() {

    }

    //2.創建類的唯一實例,使用private、static 修飾
    /**
     * 靜態成員屬於類所有,在類加載的時候他就會執行,不管用戶是否去調用這個實例,
     * 它都已經加載了,只要類加載了它就加載了。所以我們稱之爲餓漢模式,它要早些吃飽
     */
    private static Singleton instance = new Singleton();

    //3.提供一個用於獲取實例的方法,使用public、static 修飾
    public static Singleton getInstance() {
        return instance;
    }

    //-----------------------------懶漢模式----------------------------
    /**
     * 懶漢式的特點是加載類的時候比較快,但是在運行時獲取對象的速度比較慢,
     * 因爲在用戶第一次獲取的時候會去創建唯一的實例的過程
     * 線程不安全
     */
    
    //1.將構造方法私有化,不允許外部直接創建對象
    private Singleton() {

    }

    //2.聲明類的唯一實例,使用private、static 修飾
    /**
     * 在第一個用戶獲取時纔會去創建實例,後期用戶再次獲取時不會再次創建
     * 實例,所以稱之爲懶漢式
     */
    private static Singleton instance;

    //3.提供一個用於獲取實例的方法,使用public、static 修飾
    public static Singleton getInstance() {
        if (null == instance) {
            synchronized (Singleton.class) {
                if (null == instance) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

             005.分析:

                           根據你的具體需求選擇不同的單例模式

                           針對懶漢式:

                                  雙重非空判斷保證了程序的健壯性:

                                         第一次判斷是爲了避免不必要的創建

                                         第二次判斷是確保在此之前沒有其他線程進入到sychronized塊創建了新實例

                                 然而問題來了,主要是在instance=new Singleton();這段代碼上。這段代碼會編譯成多條指令,大致上做了3件事:

                                 (1)給Singleton實例分配內存
                                 (2)調用Singleton()構造函數,初始化成員字段
                                 (3)將instance對象指向分配的內存(此時instance就不是null啦~)

              上面的(2)和(3)的順序無法得到保證的,也就是說,JVM可能先初始化實例字段再把instance指向具體的內存實例,也可能先把instance指向內存實例再對實例進行初始化成員字段。考慮這種情況:一開始,第一個線程執行instance=new Singleton();這句時,JVM先指向一個堆地址,而此時,又來了一個線程2,它發現instance不是null,就直接拿去用了,但是堆裏面對單例對象的初始化並沒有完成,最終出現錯誤~

              下面這種模式可以解決這種問題:

public class Singleton {
    
    private volatile static Singleton instance;

    //將默認的構造函數私有化,防止其他類手動
    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            sychronized(Singleton.class) {
                if (instance == null)
                    instance = new Singleton();
            }
        }
        return instatnce;
    }
}

            分析:

                   這裏只是對instance變量加了一個volatile關鍵字volatile關鍵字的作用是:線程每次使用到被volatile關鍵字修飾的變量時,都會去堆裏拿最新的數據。換句話說,就是每次使用instance時,保證了instance是最新的(volatile不做詳細介紹,可以自行查找資料)。
    
           注意:
                   用不用volatile,這裏給出建議性的選擇,在兩個或者更多的線程訪問的成員變量上使用volatile。當要訪問的變量已在synchronized代碼塊中,或者爲常量時,不必使用
 
            006.參考博文:

                    點擊查看參考博文
                    
                    點擊查看參考博文

發佈了39 篇原創文章 · 獲贊 30 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章