http://www.javashuo.com/article/p-eawupvdg-ba.htmljava
由輕量鎖切換到重量鎖,是發生在輕量鎖釋放鎖的期間,以前在獲取鎖的時候它拷貝了鎖對象頭的markword,在釋放鎖的時候若是它發如今它持有鎖的期間有其餘線程來嘗試獲取鎖了,而且該線程對markword作了修改,二者比對發現不一致,則切換到重量鎖。github
由於重量級鎖被修改了,因此display mark word和原來的markword不同了。多線程
怎麼補救,就是進入mutex前,compare一下obj的markword狀態。確認該markword是否被其餘線程持有。jvm
此時若是線程已經釋放了markword,那麼經過CAS後就能夠直接進入線程,無需進入mutex,就這個做用。spa
若是線程嘗試獲取鎖的時候,輕量鎖正被其餘線程佔有,那麼它就會修改markword,修改重量級鎖,表示該進入重量鎖了。.net
還有一個注意點:等待輕量鎖的線程不會阻塞,它會一直自旋等待鎖,並如上所說修改markword。線程
這就是自旋鎖,嘗試獲取鎖的線程,在沒有得到鎖的時候,不被掛起,而轉而去執行一個空循環,即自旋。在若干個自旋後,若是尚未得到鎖,則才被掛起(進入阻塞狀態),得到鎖,則執行代碼。指針
1. 檢測Mark Word裏面是否是當前線程的ID,若是是,表示當前線程處於偏向鎖對象
2. 若是不是,則使用CAS將當前線程的ID替換Mard Word,若是成功則表示當前線程得到偏向鎖,置偏向標誌位1
3. 若是失敗,則說明發生競爭,撤銷偏向鎖,進而升級爲輕量級鎖。
4. 當前線程使用CAS將對象頭的Mark Word替換爲鎖記錄指針,若是成功,當前線程得到鎖
5. 若是失敗,表示其餘線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。
6. 若是自旋成功則依然處於輕量級狀態。
7. 若是自旋失敗,則升級爲重量級鎖。
上面幾種鎖都是JVM本身內部實現,當執行synchronized同步塊的時候jvm會根據啓用的鎖和當前線程的爭用狀況,決定如何執行同步操做。
在全部的鎖都啓用的狀況下線程進入臨界區時會先去獲取偏向鎖,若是已經存在偏向鎖了,則會嘗試獲取輕量級鎖,啓用自旋鎖,若是自旋也沒有獲取到鎖,則使用重量級鎖,沒有獲取到鎖的線程阻塞掛起,直到持有鎖的線程執行完同步塊喚醒他們
偏向鎖是在無鎖爭用的狀況下使用的,也就是同步開在當前線程沒有執行完以前,沒有其它線程會執行該同步塊,一旦有了第二個線程的爭用,偏向鎖就會升級爲輕量級鎖,若是輕量級鎖自旋到達閾值後,沒有獲取到鎖,就會升級爲重量級鎖。
若是線程爭用激烈,那麼應該禁用偏向鎖。
|
重量級鎖 |
輕量級鎖 |
偏向鎖 |
適用場景 |
發生了鎖爭搶的狀況:多條線程進入同步塊並爭用鎖 |
雖然不少線程,可是沒有衝突:多條線程進入同步塊,可是線程進入時間錯開於是並未爭搶鎖 |
自始至終只有一個線程:只有一個線程進入同步塊 |
本質 |
互斥同步 |
CAS操做代替互斥同步 |
取消同步操做 |
優勢 |
不會空耗CPU |
不會阻塞 |
不阻塞,執行效率高(只有第一次獲取偏向鎖時須要CAS操做,後面只是比對ThreadId) |
缺點 |
阻塞,上下文切換,重量級操做 |
長時間獲取不到鎖空耗CPU |
適用場景太侷限。若競爭產生,會有額外的偏向鎖撤銷的消耗 |
不一樣的鎖有不一樣特色,每種鎖只有在其特定的場景下,纔會有出色的表現,java中沒有哪一種鎖可以在全部狀況下都能有出色的效率,引入這麼多鎖的緣由就是爲了應對不一樣的狀況。