Java記憶體模型之從JMM角度分析DCL

2021-07-31 05:13:47 字數 2108 閱讀 1484

dcl,即double check lock,中衛雙重檢查鎖定。其實dcl很多人在單例模式中用過,lz面試人的時候也要他們寫過,但是有很多人都會寫錯。他們為什麼會寫錯呢?其錯誤根源在**?有什麼解決方案?下面就隨lz一起來分析

我們先看單例模式裡面的懶漢式:

public

class singleton

public

static singleton getinstance()

return singleton;

}}

我們都知道這種寫法是錯誤的,因為它無法保證執行緒的安全性。優化如下:

public

class singleton

public

static synchronized singleton getinstance()

return singleton;

}}

優化非常簡單,就是在getinstance方法上面做了同步,但是synchronized就會導致這個方法比較低效,導致程式效能下降,那麼怎麼解決呢?聰明的人們想到了雙重檢查 dcl:

public

class singleton

public

static singleton getinstance()}}

return singleton;

}}

就如上面所示,這個**看起來很完美,理由如下:

如果檢查第乙個singleton不為null,則不需要執行下面的加鎖動作,極大提高了程式的效能;

如果第乙個singleton為null,即使有多個執行緒同一時間判斷,但是由於synchronized的存在,只會有乙個執行緒能夠建立物件;

當第乙個獲取鎖的執行緒建立完成後singleton物件後,其他的在第二次判斷singleton一定不會為null,則直接返回已經建立好的singleton物件;

通過上面的分析,dcl看起確實是非常完美,但是可以明確地告訴你,這個錯誤的。上面的邏輯確實是沒有問題,分析也對,但是就是有問題,那麼問題出在**呢?

如果2、3發生了重排序就會導致第二個判斷會出錯,singleton != null,但是它其實僅僅只是乙個位址而已,此時物件還沒有被初始化,所以return的singleton物件是乙個沒有被初始化的物件,如果這時候有乙個執行緒對singleton進行訪問,得到的是乙個沒有被初始化的singleton物件

通過上面的闡述,我們可以判斷dcl的錯誤根源在於步驟4:

singleton =new singleton();
知道問題根源所在,那麼怎麼解決呢?有兩個解決辦法:

解決方案依據上面兩個解決辦法即可。

對於上面的dcl其實只需要做一點點修改即可:將變數singleton生命為volatile即可:

public

class singleton

public

static singleton getinstance()}}

return singleton;

}}

當singleton宣告為volatile後,步驟2、步驟3就不會被重排序了,也就可以解決上面那問題了。

該解決方案的根本就在於:利用classloder的機制來保證初始化instance時只有乙個執行緒。jvm在類初始化階段會獲取乙個鎖,這個鎖可以同步多個執行緒對同乙個類的初始化。

public

class singleton

public

static singleton getinstance()

}

這種解決方案的實質是:執行步驟2和步驟3重排序,但是不允許其他執行緒看見。

解釋:

因為內部靜態類是要在有引用了以後才會裝載到記憶體的。所以在你第一次呼叫getinstance()之前,singletonholder是沒有被裝載進來的,只有在你第一次呼叫了getinstance()之後,裡面涉及到了return return singletonholder.instance; 產生了對singletonholder的引用,內部靜態類的例項才會真正裝載。這也就是懶載入的意思。

java記憶體模型 JMM

1 原子性 atomicity 不可中斷的操作,即使是多個執行緒一起執行的時候,也不會被其他執行緒干擾 2 可見性 visibility 當乙個執行緒修改了某乙個共享變數的值,其他執行緒是否能夠立即知道這個修改 3 有序性 ordering 重排指令可以保證序列語義一致,但沒有義務保證多執行緒之間的...

JMM記憶體模型

依賴於jvm排程作業系統,由作業系統建立乙個核心執行緒klt,核心執行緒最終才會被cpu排程 jvm結構 jmm八大原子操作 volatitle如何保證可見性?匯流排鎖 當被volatitle修飾的變數想要修改主記憶體的資料時,會傳送一條帶有lock字首的彙編指令到匯流排上,此時它會鎖住匯流排,禁止...

JMM記憶體模型

mesi快取一致性協議 多個cpu從主記憶體讀取同乙個資料到各自的快取記憶體,當其中的乙個cpu修改了快取裡的資料,該資料會馬上同步回主記憶體,其它cpu通過匯流排嗅探機制可感知到資料的變化從而將自己快取裡的資料失效 lock unlock 匯流排加lock變成序列,效率降低 store前加lock...