常見設計模式—單例模式(Singleton)

前言

好久沒寫東西了,但是想着無論什麼事還是要堅持自己初心要堅持的東西。寫東西不能斷!

對於常用的23種設計模式,這裏筆者會根據自己學習和出現頻率、重要程度進行學習記錄吧。並且每種設計模式可能會根據暫時需求側重學習深淺。

單例模式

有很多直接把單例分成很多種,這裏我就分兩個大類(餓漢懶漢)在這裏說啦。

單例模式(Singleton Pattern)是設計模式中最簡單的模式之一,屬於創建型模式。這種設計模式主要是類的對象只有一個實例,不需要每次new 創造。而我們要做的的就是確保這個對象創建的唯一。然後根據一些特徵進行優化創建以及訪問改類。

而單例模式也有很多的應用,比如很多驅動例如攝像頭、打印機等等,而在javaweb中的spring有很多配置文件,掌控全局,同樣也是單例的。

對於單例,主要是全局只有這麼一個對象。對了它的理解,這裏筆者打幾個有可能不太恰當的理解,目的在於幫助理解記憶,如果有錯誤還請指正。

個人可能不太恰當的理解

  • 一定程度上單例模式和普通的模式有可能是一根根火柴打火機的差別。火柴想燃燒每次都要一根火柴摩擦燃燒爲灰燼作爲代價。而一個打火機可以持續供給。同樣如果你只想在某個線程某個類想要得到執行改類的一個方法,如果這個類佔用的內存、空間巨大,耗費的時間也很大的話,如果頻繁創造是一筆很大的負擔,那樣就不如咱們就創建好一個然後供我們使用就好啦。
  • 一定程度又像是他人和個人的區別。每個人都可能幹過相同的事情,可能很多人都在校園的跑道上跑過步,但他們並不是你,你也可能在操場上跑過很多圈。但只有你知道你的思考。同樣單例模式在一定程度可以看作一個人生,它可能有很多職能,也可能幹過很多事、重複過很多事。但它可能對自己有從始至終的思考和記錄。它可能有些全局參數記錄着程序從始至終一些狀態、信息等等。也就是它一直是它,你也一直是你獨特的自己
    在這裏插入圖片描述
  • 其他等等

至於單例模式的優缺點,這裏就不作詳細介紹了。無非是關於性能職能時間空間拓展性等方面的一些討論。這個可以參考不同人的不同理解。本文主要記錄單例模式的實現方面。而同樣單例模式實現上分爲餓漢式懶漢式
在這裏插入圖片描述

單例模式創建要求

  • 某個類只能有一個實例(構造器私有化)
  • 它必須自行創建這個實例(含有一個改類的靜態變量來保存這個唯一的實例)
  • 自行向整個系統提供這個實例(直接暴露或者用靜態變量的get方法)

餓漢式

直接創建對象,不存在線程安全問題 。對於餓漢式的實現是相對簡單和容易的。在實際遇到這種類似的思想其實也很多。
理解:

  • 這個餓漢式就好比雙十一,你一下把你家裏感覺可能缺的日後可能需要(也不一定需要的)全部給買了。比如啥洗衣液屯、棉衣棉鞋棉被日後可能要買也買。然後啥微波爐、烤箱我感覺可能以後會用我也買買買。買買買。這一下就買全了。但是:
    在這裏插入圖片描述
  • 同樣在實際的生產,我們一個項目中可能有很多這樣單例的存在,如果一次性啓動的話對時間花銷真的是太大。可能對程序的壓力和體驗都很差。所以一般很少直接使用。這種體驗,像極了你打開某個網頁等待JavaScript和css的過程。

餓漢式的幾種實現方式
01 . 直接實例化餓漢式(簡潔直觀)

/**
 * 餓漢式
 * 直接創建實例,不管是否需要這個對象
 */
public class singleton1 {
    public  static  final singleton1 INSTANCE = new singleton1();
    private singleton1()
    {}
}

02 . 枚舉式(最簡潔)

public enum singleton2 {
   INSTANCE
}

03 . 靜態代碼塊餓漢式(適合複雜實例化)

public class singleton3 {
    public static final singleton3 INSTANCE;
    static {
        /***
         * 有可能一些數據需要配置,需要從類加載器加載
         * 例如從某xxx.properties加載某些配置信息,我們只需更改配置文件不需要更改代碼
         */
        INSTANCE=new singleton3();
    }
    private  singleton3()
    {}
}

懶漢式

我們知道餓漢式在早早把對象創建好,在執行一些其他邏輯時候不存在線程安全的問題。但是懶漢式就不一樣啦,延遲創建對象可能會有線程安全問題。
01 .線程不安全(適合單線程)

/***
 * 懶漢式:構造器私有化
 * 靜態變量保存實例
 * 提供一個靜態方法,獲取這個實例對象
 */
public class singleton4 {
    private static singleton4 instance;
    private  singleton4(){}
    public  static singleton4 getInstance()
    {
        if (instance==null)
            instance = new singleton4();
        return  instance;
    }
}

對於這種單線程沒問題,但是如果如果兩個或多個線程來同時訪問instance如果都爲null同時申請的時候會遇到下圖等之類問題,這違背了單例模式的設計原則並且可能會對系統數據造成錯誤。
在這裏插入圖片描述
02 .線程安全(適用於多線程)
怎麼優化上述的懶漢式單例模式呢?既然不允許多個線程這樣同時訪問,那麼咱們給它上個鎖不久行了嘛!

public class singleton5 {
    private static singleton5 instance;
    private  singleton5(){}
    public  static singleton5 getInstance()
    {
        synchronized (singleton5.class)
        {
            if (instance == null)
                instance = new singleton5();
        }
        return  instance;
    }
}

但是這樣有啥問題呢?就是我獲取這個單例對象的時候,被上鎖了。你氣不氣?我們就是在創建這個單例時候多線程可能出現點問題,咱麼獲取的時候不存在線程安全問題啊。。你爲啥獲取也要鎖??

這就好比,咱們都喜歡看美女,跟美女說話要排隊一個一個來,只能一個在一個時刻,但是你看美女不需要排隊啊!!!
在這裏插入圖片描述
03 .線程安全推薦版(適用於多線程)
對於上述存在的問題,咱們只需要雙重判定就行了。

  • 首先判斷instance是不是爲null。如果不是null說明被實例化過直接返回即可!nice!
  • 如果instance爲null?上鎖之後再判空實例化。
public class singleton6 {
    private static singleton6 instance;
    private singleton6(){}
    public  static singleton6 getInstance()
    {
        if(instance==null) {
            synchronized (singleton6.class) {
                if (instance == null)
                    instance = new singleton6();
            }
        }
        return instance;
    }
}

兩個判斷爲null?爲啥要兩個?

  • 第一個:用來判斷是否爲空需要上鎖new 實例。
  • 第二個:上鎖後雖然只有一個會執行當前方法,但是很可能都爲null的時候兩個或多個都爲null上鎖的想構造。然後後面線程在等待同時前面線程構造好了,那麼它就不需要再去構造這個instance啦!直接不操作就行了。

就這樣,稍微完美的方法就這樣產生了。

04 .靜態內部類形式(適用於多線程)
上面的方法可能有些複雜,而靜態內部類也是個好方式。主要是靜態內部類和外部類不是一起加載的,並且你去調用它的時候他就會初始化,並且類加載是線程安全的,這個不需要考慮線程安全問題。當加載完之後你就可以直接拿啦。這樣也能達到延遲加載的作用。

這個更詳細你可以自己研究靜態變量(類變量)、靜態內部類等等加載順序,研究下`static關鍵字。

public class singleton7 {
    private singleton7(){}
    private static class inner
    {
        private static final singleton7 instance=new singleton7();
    }
    public static singleton7 getInstance()
    {
        return inner.instance;
    }
}

結語

學習參考尚學堂單例講解以及百科、菜鳥教程等等,有些區別但是大部分實現都是相似的,帶上個人理解分享給大家。如果有問題和疏漏還請指教!

歡迎交流學習、歡迎關注微信公衆號bigsai
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章