天天看點

鎖的更新

Java SE 1.6為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級鎖”,在Java SE 1.6中,鎖一共有4種狀态,級别從低到高依次是:無鎖狀态、偏向鎖狀态、輕量級鎖狀态和重量級鎖狀态,這幾個狀态會随着競争情況逐漸更新。目的是為了提高獲得鎖和釋放鎖的效率

1. 偏向鎖

HotSpot 的作者經過研究發現,大多數情況下,鎖不僅不存在多線程競争,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。當一個線程通路同步塊并擷取鎖時,會在對象頭和棧幀中的鎖記錄裡存儲鎖偏向的線程ID,以後該線程在進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,隻需簡單地測試一下對象頭的Mark Word裡是否存儲着指向目前線程的偏向鎖。如果測試成功,表示線程已經獲得了鎖。如果測試失敗,則需要再測試一下Mark Word中偏向鎖的辨別是否設定成1(表示目前是偏向鎖):如果沒有設定,則使用CAS競争鎖;如果設定了,則嘗試使用CAS将對象頭的偏向鎖指向目前線程。

(1)偏向鎖撤銷

偏向鎖使用了一種等到競争出現才釋放鎖的機制,是以當其他線程嘗試競争偏向鎖時,持有偏向鎖的線程才會釋放鎖。偏向鎖的撤銷,需要等待全局安全點(在這個時間點上沒有正在執行的位元組碼)。它會首先暫停擁有偏向鎖的線程,然後檢查持有偏向鎖的線程是否活着,如果線程不處于活動狀态,則将對象頭設定成無鎖狀态;如果線程仍然活着,擁有偏向鎖的棧會被執行,周遊偏向對象的鎖記錄,棧中的鎖記錄和對象頭的Mark Word要麼重新偏向于其他線程,要麼恢複到無鎖或者标記對象不适合作為偏向鎖,最後喚醒暫停的線程。

2. 輕量級鎖

(1)輕量級鎖加鎖

線程在執行同步塊之前,JVM會先在目前線程的棧桢中建立用于存儲鎖記錄的空間,并将對象頭中的Mark Word複制到鎖記錄中,官方稱為Displaced Mark Word。然後線程嘗試使用CAS将對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,目前線程獲得鎖,如果失敗,表示其他線程競争鎖,目前線程便嘗試使用自旋來擷取鎖。

(2)輕量級鎖解鎖

​ 輕量級解鎖時,會使用原子的CAS操作将Displaced Mark Word替換回到對象頭,如果成功,則表示沒有競争發生。如果失敗,表示目前鎖存在競争,鎖就會膨脹成重量級鎖。

鎖的更新

因為自旋會消耗CPU,為了避免無用的自旋(比如獲得鎖的線程被阻塞住了),一旦鎖更新成重量級鎖,就不會再恢複到輕量級鎖狀态。當鎖處于這個狀态下,其他線程試圖擷取鎖時,都會被阻塞住,當持有鎖的線程釋放鎖之後會喚醒這些線程,被喚醒的線程就會進行新一輪的奪鎖之争。

鎖的優缺點對比

鎖的更新