java多線程(七)---鎖的升級(jí)與對(duì)比(偏向鎖-輕量級(jí)鎖)

移步java多線程系列文章
Java SE 1.6為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”。

1 鎖的狀態(tài)

  • 在Java SE 1.6中,鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無鎖狀態(tài)、偏向鎖狀態(tài)、輕量級(jí)鎖狀態(tài)重量級(jí)鎖狀態(tài),這幾個(gè)狀態(tài)會(huì)隨著競爭情況逐漸升級(jí)。
  • 鎖可以升級(jí)但不能降級(jí),意味著偏向鎖升級(jí)成輕量級(jí)鎖后不能降級(jí)成偏向鎖。
  • 這種鎖升級(jí)卻不能降級(jí)的策略,目的是為了提高獲得鎖和釋放鎖的效率,

2 偏向鎖

  • HotSpot 的作者經(jīng)過研究發(fā)現(xiàn),大多數(shù)情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖。
  • 當(dāng)一個(gè)線程訪問同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線程ID,以后該線程在進(jìn)入和退出同步塊時(shí)不需要進(jìn)行CAS操作來加鎖和解鎖,只需簡單地測試一下對(duì)象頭的Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。
  • 如果測試成功,表示線程已經(jīng)獲得了鎖。
  • 如果測試失敗,則需要再測試一下Mark Word中偏向鎖的標(biāo)識(shí)是否設(shè)置成1(表示當(dāng)前是偏向鎖):如果沒有設(shè)置,則使用CAS競爭鎖;如果設(shè)置了,則嘗試使用CAS將對(duì)象頭的偏向鎖指向當(dāng)前線程。

2.1 偏向鎖的撤銷

  • 偏向鎖使用了一種等到競爭出現(xiàn)才釋放鎖的機(jī)制,所以當(dāng)其他線程嘗試競爭偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖。
  • 偏向鎖的撤銷,需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒有正在執(zhí)行的字節(jié)碼)。
  • 它會(huì)首先暫停擁有偏向鎖的線程,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動(dòng)狀態(tài),則將對(duì)象頭設(shè)置成無鎖狀態(tài);
  • 如果線程仍然活著,擁有偏向鎖的棧會(huì)被執(zhí)行,遍歷偏向?qū)ο蟮逆i記錄,棧中的鎖記錄和對(duì)象頭的Mark Word要么重新偏向于其他線程,要么恢復(fù)到無鎖或者標(biāo)記對(duì)象不適合作為偏向鎖,最后喚醒暫停的線程。
    如下圖
    線程1演示了偏向鎖初始化的流程,線程2演示了偏向鎖撤銷的流程。
    qq_pic_merged_1532534792487.jpg

2.2 關(guān)閉偏向鎖

  • 偏向鎖在Java 6和Java 7里是默認(rèn)啟用的,但是它在應(yīng)用程序啟動(dòng)幾秒鐘之后才激活,
  • 如有必要可以使用JVM參數(shù)來關(guān)閉延遲:-XX:BiasedLockingStartupDelay=0。
  • 如果你確定應(yīng)用程序里所有的鎖通常情況下處于競爭狀態(tài),可以通過JVM參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,那么程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。

3 輕量級(jí)鎖

3.1 輕量級(jí)鎖加鎖

  • 線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲(chǔ)鎖記錄的空間,并將對(duì)象頭中的Mark Word復(fù)制到鎖記錄中,官方稱為Displaced Mark Word。
  • 然后線程嘗試使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當(dāng)前線程獲得鎖,如果失敗,表示其他線程競爭鎖,當(dāng)前線程便嘗試使用自旋來獲取鎖。

3.2 輕量級(jí)鎖解鎖

輕量級(jí)解鎖時(shí),會(huì)使用原子的CAS操作將Displaced Mark Word替換回到對(duì)象頭,如果成功,則表示沒有競爭發(fā)生。如果失敗,表示當(dāng)前鎖存在競爭,鎖就會(huì)膨脹成重量級(jí)鎖。
如下圖
兩個(gè)線程同時(shí)爭奪鎖,導(dǎo)致鎖膨脹的流程圖。

qq_pic_merged_1532535638164.jpg

  • 因?yàn)樽孕龝?huì)消耗CPU,為了避免無用的自旋(比如獲得鎖的線程被阻塞住了),一旦鎖升級(jí)成重量級(jí)鎖,就不會(huì)再恢復(fù)到輕量級(jí)鎖狀態(tài)。
  • 當(dāng)鎖處于這個(gè)狀態(tài)下,其他線程試圖獲取鎖時(shí),都會(huì)被阻塞住,當(dāng)持有鎖的線程釋放鎖之后會(huì)喚醒這些線程,被喚醒的線程就會(huì)進(jìn)行新一輪的奪鎖之爭。

4 鎖的優(yōu)缺點(diǎn)對(duì)比

優(yōu)點(diǎn) 缺點(diǎn) 適用場景
偏向鎖 加鎖和解鎖不需要額外的消耗,和執(zhí)行非同步方法相比僅存在納秒級(jí)的差距 如果線程間存在鎖競爭,會(huì)帶來額外的鎖撤銷的消耗 適用于一個(gè)線程訪問同步塊場景
輕量級(jí)鎖 競爭的線程不會(huì)阻塞,提高了程序的響應(yīng)速度 如果始終得不到鎖競爭的線程,使用自旋會(huì)消耗CPU 追求響應(yīng)時(shí)間,同步塊執(zhí)行速度非常快
重量級(jí)鎖 線程競爭不使用自旋,不會(huì)消耗CPU 線程阻塞,響應(yīng)時(shí)間緩慢 追求吞吐量 同步塊執(zhí)行速度較長

參考

《java并發(fā)編程的藝術(shù)》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容