單例模式的雙重檢測鎖與volatile禁止重排

2022-06-05 22:06:13 字數 1210 閱讀 2289

public class demo

public static demo getinstance()

return instance;

}}

上面單例實現方式在單執行緒訪問下沒有問題,但是在併發訪問時,會產生多個物件。

如程式啟動 a執行緒獲取instance執行完if判斷為null後,執行緒b獲取到cpu執行權並完成if判斷和instance例項化得到時列物件,當a執行緒再次獲得執行權後執行if中語句,又會建立乙個新物件並對instance賦值,此時a,b得到的為兩個不同的物件。

public class demo

public static synchronized demo getinstance()

return instance;

}}

使用synchronized關鍵字加鎖(鎖物件是demo.class物件),保證了每次只會有乙個執行緒執行方法。

雖然上述程式已不會出現使用問題,但是直接對整個方法進行鎖定太過粗暴,作為高雅人士應該使用更優雅的方式實現鎖定。

public class demo

public static demo getinstance()}}

return instance;

}}

對要逐個執行的功能鎖定,對鎖細化節省同步時間提高執行效率。

此時功能已經接近完美,但是對於超高併發的任務來說並不安全

如instance=new demo()這句**,編譯後將會拆分出多個指令(以下只取三個關鍵指令描述)

初始化物件成員變數並賦預設值

將為我們要賦值給成員變數的值替換掉預設值

將物件的位址賦值給對應的引用

由於cpu存在指令重排的優化機制,在多執行緒訪問時可能會影響到我們的執行結果,如下將2,3指令掉個順序

初始化物件成員變數並賦預設值

將物件的位址賦值給對應的引用

將為我們要賦值給成員變數的值替換掉預設值

public class demo

public static demo getinstance()}}

return instance;

}}

使用volatile修飾instance物件,那麼cpu對該物件的操作將不會再進行指令重排,確保了物件初始化完成,並最後一步返回物件的位址給引用,達到萬無一失的情況。

雙重檢測單例模式

在多執行緒場景下,當乙個執行緒判斷instance為null時 他會新建乙個例項,那麼問題來了,當a執行緒發現物件例項為空時,準備 新建乙個例項,這時cpu輪詢到b執行緒,b執行緒也察覺物件例項為空,它也會新建乙個例項,這樣就破壞了單例模式。首先物件例項必須是全域性共享的,用volatile修飾,然...

雙重檢測鎖單例模式指令重排問題

解決方案 相信大多數同學在面試當中都遇到過手寫單例模式的題目,那麼如何寫乙個完美的單例是面試者需要深究的問題,因為乙個嚴謹的單例模式說不定就直接決定了面試結果,今天我們就要來講講看似執行緒安全的雙重檢測鎖單例模式中可能會出現的指令重排問題。乍一看下面單例模式沒啥問題,還加了同步鎖保證執行緒安全,從表...

單例模式 雙重檢查鎖

單例模式分為餓漢式和懶漢式。餓漢式是事先分配記憶體,提前建立。這樣的方式為到位占用資源,當這種比較多時,會占用很多記憶體。懶漢式是在被呼叫的時候進行。這種在併發時又會導致問題。比較穩妥的辦法是在懶漢式的基礎上加上鎖,然後進行雙重檢查,這種springioc容器式單例也是用這種雙重檢查來避免執行緒衝突...