1. 程式人生 > >《深入理解Java虛擬機》筆記04 -- 並發鎖

《深入理解Java虛擬機》筆記04 -- 並發鎖

server som 競爭 包括 系統 cap cnblogs blocks 嘗試

Java虛擬機在操作系統層面會先盡一切可能在虛擬機層面上解決競爭關系,盡可能避免真實的競爭發生。同時,在競爭不激烈的場合,也會試圖消除不必要的競爭。實現這些手段的方法包括:偏向鎖、輕量級鎖、自旋鎖、鎖消除、鎖膨脹等

1. 偏向鎖

偏向鎖是JDK1.6提出的一種鎖優化方式。其核心思想是:如果程序沒有競爭,則取消之前已經取得鎖的線程同步操作。也就是說,若某一鎖被線程獲取後,便進入偏向模式,當線程再次請求這個鎖時,無需再進行相關的同步操作,從而節省了操作時間。如果在此之間有其他線程進行了鎖請求,則鎖退出偏向模式。在JVM中使用-XX:+UseBiasedLocking 可以設置啟用偏向鎖。

偏向鎖在鎖競爭激烈的場合沒有太強的優化效果。

2. 輕量級鎖

如果偏向鎖失敗,Java虛擬機會讓線程申請輕量級鎖。輕量級鎖在虛擬機內部,使用一個稱為BasicObjecLock的對象實現,這個對象內部由一個BasicLock對象和一個持有該鎖的Java對象指針組成。BasicobjectLock對象放置在Java棧的棧幀中。

首先,BasicLock通過set_displaced_header()方法備份了原對象的Mark Word。接著,使用CAS操作,嘗試將BasicLock的地址復制到對象頭的Mark Word。如果復制 成功,那麽加鎖成功。如果加鎖失敗,那麽輕量級鎖就有可能被膨脹為重量級鎖。

3. 鎖膨脹

當輕量級鎖失敗時,就會膨脹成重量級鎖。輕量級鎖加鎖失敗後,虛擬機會執行以下操作:1. 廢棄前面BasicLock備份的對象頭信息。2. 通過inflate()方法進行鎖膨脹,其目地是獲得對象的ObjectMonitor 3. 使用enter()方法嘗試進入鎖。

4. 自旋鎖

鎖膨脹後,進入ObjectMonitor的enter(),純種很可能會在操作系統層面被掛起,這樣線程上下文切換的性能損失就比較大。因此,在鎖膨脹之後,虛擬機會做最後的爭取,希望線程可以盡快進入臨界區而避免被操作系統掛起。一種較為有效的手段就是使用自旋鎖。

自旋鎖可以使線程在沒有取得鎖時,不被掛起,而轉去執行一個空循環(即所謂的自旋),在若十個空循環後,線程如果可以獲得鎖,則繼續招待。若依然不能獲得鎖,才會被掛起。不太適用於鎖競爭激烈的場合。

在JDK 1.6中,Java虛擬機提供 -XX:+UseSpinning 參數來開戶自旋鎖,使用 -XX:PreBlockSpin 參數來設置自旋鎖的等待次數。

在JDK1.7中,自旋鎖參數被取消。自旋鎖總是會執行,自旋次數也由虛擬機自行調整。

5. 鎖消除

鎖消除是Java虛擬機在JIT編譯時,對過對運行上下文的掃描,運用逃逸技術,去除不可能存在共享資源競爭的鎖。比如:

public void someBusiness(){
    ......
    StringBuffer sb = new StringBuffer();
    sb.append("XXX");  
    ...... 
}

StringBuffer中的方法都是有synchronized的,但是這裏只是方法內的局部變量,不可能存在鎖競爭。

逃逸分析和鎖消除分別可以使用參數 -XX:+DoEscapeAnalysis 和 -XX:+EliminateLocks 開啟(鎖消除必須工作在 -server模式下)

《深入理解Java虛擬機》筆記04 -- 並發鎖