單例模式相信大家都有遇到,面試中出現概率還是比較高的,從最基礎的加synchronized鎖到雙重檢查鎖,再到加上volatile關鍵字,小小的乙個單例模式裡還是存在不少問題的,下面先看**:
基礎的:
雙重檢查的:
這一步的改善比較好理解,在多執行緒的情況下第乙個方法的效率會低於第二個方法的,第乙個方法會導致每個呼叫方法的執行緒都阻塞,直到上個執行緒結束呼叫。
而第二個方法只會在物件還沒例項化的情況下被阻塞,也就是只要物件被例項化了,後面每個執行緒都可以第一時間拿到物件無需等待,效率比上面高了不少。
當然了,下面方法synchronized塊內部的if判斷不能不加,這就是雙重檢查的意思所在,至於為什麼必須要加,這裡就不多說了。
這裡就涉及到虛擬機器的重排序了,簡單說明下:在虛擬機器層面,為了盡可能減少記憶體操作速度遠慢於cpu執行速度所帶來的cpu空置的影響,虛擬機會按照自己的一些規則將程式編寫順序打亂——即寫在後面的**在時間順序上可能會先執行,而寫在前面的**會後執行——以盡可能充分地利用cpu。
也就導致了實際**執行的順序並不一定是按照我們所看到或者想到的**順序來執行的,
這裡會發生什麼呢,在最終呼叫建構函式例項化物件前,物件的值可能會被賦值為非空,但是此時的物件狀態是錯誤的,在多執行緒情況下,如果這個錯誤狀態的物件被其他執行緒拿到,那就是有問題的。
所以我們引入了最後一步改善,就是用volatile關鍵字修飾single變數,
防止重排序是volatile關鍵字的乙個重要作用,我們這裡用volatile修飾single變數,上面的情況也就不會發生。
總結:繞來繞去無非是為了保證物件在記憶體中只存在乙個,而且還要獲取的效率高,也就面試筆試會這樣幹了,實際情況中spring較好的解決了這個問題,springioc中物件預設都是單例的(餓漢式的),spring會在程式開始執行前將指定的物件建立好,也不會存在上面的多執行緒安全問題了..
單例模式的那些事
寫在前面 單例singleton設計模式,老生常談的乙個設計模式。但你真的用對了麼?用的姿勢很重要!單例顧名思義就是只產生乙個例項物件。那怎樣保證單一呢?把目標類提供給外部建立例項物件的能力收回,即建構函式設為私有,然後內部提供乙個生成例項靜態方法。設計成單例模式,有各種各樣的實現方式。2.1 餓漢...
Java單例模式的幾種坑
在乙個jvm程序中,乙個類對應的例項物件有且只有乙個。因為在乙個程式中,有些業務邏輯和流程是重複的 通用的,沒有必要在每次執行時再進行new相同物件的操作。只進行一次new操作,沒有物件的頻繁建立和 提高了jvm的執行響應速度。尤其是在高併發的情況下,對程式的執行有很大的提公升。1 在多執行緒的場景...
Python單例模式的採坑
classa instance none def new cls,args,kwargs if cls.instance is none cls.instance super new cls print cls.instance return cls.instance def init self p...