本篇文章是源於工做中遇到了一個與單例模式相關的問題。並由此對單例模式做一個複習總結。
在一次開發過程當中,控制檯拋出了一個關於寫入數據庫時,主鍵衝突的異常。最後排查問題後主要是由於如下三點: 1)獲取數據庫鏈接的方式並非單例的; 2)加載頁面時同時請求了相同的接口兩次; 3)主鍵採用的是自動增加。 固然這裏如果須要快速解決bug,只需解決問題2,既加載頁面時只請求一次該接口。 可是問題1纔是潛藏更深的隱患。由於較複雜的業務將會佔用過多的多餘鏈接,影響系統性能,並可能再次致使上述問題。
在瞭解單例模式以前,咱們應該先要明白兩個問題,什麼叫設計模式,設計模式與單例模式又是怎樣的關係。 設計模式是面向對象的軟件開發員,經過漫長的試驗和錯誤總結出來的一套解決通常問題的方案。 而單例模式,就是其中一種針對一類問題的解決方案。 這就比如設計模式是獨孤九劍,而單例模式和其它不一樣的設計模式就比如破劍式、破刀式。 破劍式就是破解普天下各門各派劍法的招式。那麼單例模式又是解決什麼問題的呢?而且是如何解決的呢? 單例模式提供了一種最佳的建立對象的方式: 一個單一的類,負責建立本身的對象,同時確保只建立一個對象,而且提供了惟一一個訪問此對象的方法,且不須要再實例化該對象。
如今已經明白單例模式使用的要點既,一個類僅一個實例,而且提供訪問對象惟一全局方法。 那麼單例模式主要解決的是什麼呢? 一個全局的類頻繁的建立和消耗。 如何解決的呢? 判斷是否已有其單例,有則返回,沒有則建立。 什麼時候使用呢? 當咱們想控制實例化數量,節省系統資源時。 現實場景: 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起
構造器是私有的。 一般不使用第一、2種,通常使用第3種,只有明確須要懶加載時使用第5種, 須要反序列化建立時能夠嘗試第6種,有其它特殊需求時,能夠考慮第4種。
https://www.runoob.com/design...線程
https://www.runoob.com/design...設計
https://baike.baidu.com/item/...code
如有不足,歡迎指正。 求知若渴,虛心若愚。