您好,登錄后才能下訂單哦!
本篇內容介紹了“web設計模式中的單例模式是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
單例模式 (Singleton Pattern)使用的比較多,比如我們的 controller 和 service 都是單例的,但是其和標準的單例模式是有區別的。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
單例模式的結構很簡單,只涉及到一個單例類,這個單例類的構造方法是私有的,該類自身定義了一個靜態私有實例,并向外提供一個靜態的公有函數用于創建或獲取該靜態私有實例。
單例模式分為懶漢單例和餓漢單例;餓漢單例代碼很簡單,顧名思義,餓漢單例就是類初始化的時候就將該單例創建,示例代碼如下:
public class Singleton { private static final Singleton singleton = new Singleton(); //限制產生多個對象 private Singleton(){ } //通過該方法獲得實例對象 public static Singleton getSingleton(){ return singleton; } //類中其他方法,盡量是 static public static void doSomething(){ } }
但是懶漢單例就不那么簡單了,懶漢單例是在訪問這個類的實例的時候先判斷這個類的實例是否創建好了,如果沒創建好就要先創建這個單例。也就是說懶漢單例是第一次訪問的的時候創建單例,而不是初始化階段。這將會導致一個問題,如果在多線程場景下,多個線程同時訪問這個單例都發現其未被創建,那么這些線程就會分別創建實例,那么這個單例模式就不那么單例了——實例被多次創建。在阿里開發手冊中有兩條就是和懶漢單例相關的,告訴我們要如何去避免這種情況,第六節的第一條 和第十二條:
(六)并發處理
1.【強制】獲取單例對象需要保證線程安全,其中的方法也要保證線程安全。
說明:資源驅動類、工具類、單例工廠類都需要注意。
【推薦】在并發場景下,通過雙重檢查鎖(double-checked locking)實現延遲初始化的優
化問題隱患,推薦解
決方案中較為簡單一種(適用于 JDK5 及以上版本),將目標屬性聲明為 volatile 型。
反例:
class Singleton { private Helper helper = null; public Helper getHelper() { if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); } return helper; } // other methods and fields... }
volatile
關鍵字的作用和雙重檢查鎖在我以往的博客中介紹過,文章地址https://mp.weixin.qq.com/s/r52hmD71TtiJjlOzQUvRlA
這篇博客介紹了并發的一些知識,小伙伴有空可以讀一讀。在這里 volatile
關鍵字的作用就是保證數據的可見性,雙重檢查鎖是提高代碼性能。下面我們分析一下手冊中的反例:
其中它的雙重檢測鎖指的是這段代碼:
if (helper == null) synchronized(this) { if (helper == null) helper = new Helper(); }
這里如果不用雙重檢測鎖的話只能在整個 getHelper
方法上上鎖,因為這個方法必須要保證在并發情況下只有一個線程會執行helper = new Helper();
,這段代碼。也就是說代碼 會成為這樣:
public synchronized Helper getHelper() { if (helper == null) { if (helper == null) helper = new Helper(); } return helper; }
整個方法上鎖性能明顯是不好的,鎖的粒度變大了;雙重檢查鎖里面為什么要做兩次 if 判斷呢,這個問題留給讀者思考,并不是特別難的問題。但是反例里面沒有考慮到可見性的問題——假設a線程和b線程同時訪問 getHelper
方法,然后 b 線程被阻塞住,a線程發現helper
未被實例化,于是執行new方法,然后釋放鎖;此時b線程進來,或許我們直觀的感受是b線程發現屬性被實例化直接返回helper
,但實際上不是,當一個線程修改了線程共享的公共資源的時候(此處是helper屬性)其他線程未必會被通知到屬性被修改,因此b線程有可能發現 helper
還是null 也有可能b線程知道 helper 被賦值了。使用volatile
就可以避免這種情況的發生。因此正確的代碼應該是這樣的:
class Singleton { private volatile Helper helper = null; public Helper getHelper() { ······ } // other methods and fields... }
“web設計模式中的單例模式是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。