淺談23種設計模式(java版)
單例模式
-
什麼是設計模式
- 設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、經過分類的、代碼設計經驗的總結,使用設計模式的目的是爲了提高代碼的可重用性,使得代碼更加符合人類的理解思維。
-
什麼是單例模式
- 單例顧名思義在整個系統中只有一個類的實例對象。
-
這有什麼好處
- 有一些對象我們確實只需要一個,例如,線程池,緩存,對話框,日誌對象等等,就只能有一個實例,如果創建多個實例,就會導致許多問題,例如程序運行異常,運行結果不一致等問題。
-
不簡單的單例模式
- 雖然單例模式聽起來簡單易懂,但是真正想實現一個單例模式也不是那麼容易的,如何保證一個對象只能實例化一次,如何保證多線程下的線程安全問題等等。
-
接下來讓我們一步一步的來實現單例模式
-
如何創建一個對象?
new Singleton();
-
如果其它對象想創建Singleton會怎麼樣? 可以再次創建嗎?
是的,當然可以
-
如果有一個類我們是否可以多次實例化它?
答案是:當然可以了, 只要你的類是公開的
-
如果不公開類會怎麼樣?
如果不公開類,那只有同一個包內的類可以實例化它,並可以實例化多次(小夥子你好摳)
-
我們可以讓外部無法實例化它嗎?
當然可以了,如果我們把它的構造方器定義爲私有的,除了它的內部類可以初始化它,其它對象都不能初始化它,當然也就不能實例化它(讓人活不了)
public Singleton{ private Singleton(){ } }
-
我覺得私有構造器是不是有點不合理?
沒錯確實有點不合理,因爲我們必須有
Singleton
類的實例纔可以調用該類的構造器,但沒有類能實例化它,所以我們得不到它的實例,這就好比 “雞生蛋,蛋生雞”,我們只能在類內部調用構造器。 -
我有個想法你認爲如何?
如果我們在類的內部創建一個靜態方法去調用它如何。
public Singleton{ private Singleton(){ } public static Singleton getInstance(){ } }
-
爲什麼 調用
getInstance()
不用對象名而用類名?因爲
getInstance()
方法是靜態的,靜態方法是屬於類而非屬於對象的,所以調用用它只能用類名。 -
有意思,如果把上面的組合在一起會如何?
我們就能夠在
getInstance()
方法中愉快的進行實例的創建了。public Singleton{ private Singleton(){ } public static Singleton getInstance(){ return new Singleton(); } }
-
我們是否解決了實例化問題?
對,我們已經可以實例化對象了,但也僅僅是實例化對象,並沒有保證對象唯一。
-
如何保證對象的唯一性?
我們改造一下上面的代碼
//懶漢模式 public Singleton{ //保存實例化的對象 private static Singleton singleton = null; private Singleton(){ } public static Singleton getInstance(){ if(singleton == null){ return new Singleton(); } else{ return singleton; } } }
//餓漢模式 public Singleton{ //類加載時候就創建對象(因爲是靜態變量所以只加載一次保證了實例唯一) private static Singleton singleton = new Singleton()l; private Singleton(){ } public static Singleton getInstance(){ return singleton; } }
以上兩種模式創建單例對象。
-
有個問題出現瞭如果在多線程情況下會如何?
我們發現在懶漢模式下可能會出現多個線程創建多個實例的情況,這就違背了我們單例原則了。
-
如何解決多線程下的安全問題?
使用
synchronized
關鍵字同步代碼塊//懶漢模式 public Singleton{ //保存實例化的對象 private static Singleton singleton = null; private Singleton(){ } //加入同步關鍵字使得同一時間只能有一個線程實例化該類 public static synchronized Singleton getInstance(){ if(singleton == null){ return new Singleton(); } else{ return singleton; } } }
但是我們只有第一次執行纔會同步,顯然上面的每次執行都同步影響效率
-
我們如何改善多線程?
使用雙重鎖 首先檢查實例是否創建,如果未創建纔會同步,並且只有第一次同步。
//懶漢模式 public Singleton{ //使用volatile關鍵字 保證當Single 創建時,多線程能正確處理 private volatile static Singleton singleton = null; private Singleton(){ } //加入同步關鍵字使得同一時間只能有一個線程實例化該類 public static Singleton getInstance(){ if(singleton == null){ synchronized (Singleton.class){ if(singleton == null){ singleton = new Singleton(); } } } return singleton; } }
-
總結
看到這裏是不是在想單例也不是那麼容易創建的,需要考慮如何保證唯一實例,多線程下如何保證實例唯一,如何優化多線程等等,但是隻要我們一步一步的學習,不要操之過急,認真思考,多加練習,再難的設計模式也能學會。 後續我會加入工廠模式繼續優化單例的創建
-