Java鎖的升級策略 偏向鎖 輕量級鎖 重量級鎖

這三種鎖是指鎖的狀態,而且是專門針對Synchronized關鍵字。JDK 1.6 爲了減小"重量級鎖"的性能消耗,引入了「偏向鎖」和「輕量級鎖」,鎖一共擁有4種狀態:無鎖狀態、偏向鎖、輕量級鎖、重量級鎖。鎖狀態是經過對象頭的Mark Word來進行標記的:安全

鎖能夠升級但不能降級,意味着偏向鎖升級成輕量級鎖後不能降級成偏向鎖,這種鎖升級卻不能降級的策略,是爲了提升得到鎖和釋放鎖的效率多線程

  1. 重量級鎖:依賴於底層操做系統的Mutex Lock,線程會被阻塞住
    • 缺點:加鎖和解鎖須要從用戶態切換到內核態,性能消耗較大
  2. 輕量級鎖:基於重量級鎖進行了優化(避免上下文切換,提升了性能),它假設多線程競爭是互相錯開的,不會發生線程阻塞,呢麼上下文切換就是多餘的
    • 第一個特色:採用了CAS操做加鎖和解鎖,因爲輕量級鎖的鎖記錄(Lock Record)是存放在對象頭和線程空間裏的,所以加鎖和解鎖不須要上下文切換,性能消耗較小
    • 第二個特色:一旦發生多線程競爭,首先基於「自旋鎖」思想,自旋CPU循環等待一段時間,不會發生上下文切換,若是仍是沒法得到鎖,就將鎖升級爲重量級鎖
  3. 偏向鎖:基於輕量級鎖進行了優化(減小屢次的加鎖和解鎖,提升了性能),它假設整個過程只有一個線程得到鎖,呢麼屢次的加鎖和解鎖就是多餘的
    • 特色:在第一次得到鎖以後不會釋放鎖,它會一直持有鎖,後續進入鎖時只需檢查一下鎖狀態和偏向線程ID是否爲本身,從而省去了屢次的加鎖和解鎖

1.偏向鎖

獲取鎖:性能

  1. 檢測對象頭的Mark Word是否爲可偏向狀態(便是否爲偏向鎖1,鎖標誌位是否爲01),若是不是,嘗試競爭鎖:嘗試CAS操做將Mark Word的線程ID設置爲當前線程ID,以表示線程得到鎖,若是失敗說明鎖已被佔用
  2. 若爲可偏向狀態,則檢查線程ID是否爲當前線程ID,若是是則表示當前線程已經持有鎖(鎖的可重入),不然說明鎖已被佔用
  3. 若是鎖已被佔用,只能撤銷偏向鎖爲無鎖狀態或輕量級鎖

釋放鎖:(偏向鎖使用了一種等到競爭出現才釋放鎖的機制,線程是不會主動釋放偏向鎖的,只有當其餘線程競爭偏向鎖時,持有偏向鎖的線程纔會釋放鎖。這樣作的好處是大大減小了加鎖和解鎖的次數,提高性能)優化

  1. 偏向鎖的撤銷須要等待全局安全點(在這個時間點沒有正在執行的字節碼),暫停擁有偏向鎖的線程,檢查持有偏向鎖的線程是否還活着
  2. 若是線程掛了,則將對象頭設置成無鎖狀態;若是線程仍然活着,則將對象頭設置爲輕量級鎖(鎖的升級),最終輕量級鎖必定會被釋放

2.輕量級鎖

獲取鎖:操作系統

  1. 檢測對象頭的Mark Word是否爲輕量級鎖(鎖標誌位爲00),若是不是,嘗試競爭鎖:JVM首先在當前線程的棧幀中創建一個鎖記錄(Lock Record),用於備份存儲對象頭的Mark Word(官方把這份拷貝加了一個Displaced前綴,稱爲Displaced Mark Word),而後JVM嘗試CAS操做將Mark Word更新爲指向Lock Record的指針,以表示線程得到鎖,若是失敗說明鎖已被佔用
  2. 若爲輕量級鎖,判斷對象頭的Mark Word是否指向當前線程的棧幀的Lock Record,若是是則表示當前線程已經持有鎖(鎖的可重入),不然說明鎖已被佔用
  3. 若是鎖已被佔用,當前線程便嘗試自旋CPU來獲取鎖,自旋必定次數後輕量級鎖會膨脹爲重量級鎖(鎖標誌位變成10),線程進入阻塞

釋放鎖:線程

  1. 嘗試CAS操做將Displaced Mark Word中替換回對象頭,若是成功,說明輕量級鎖釋放成功
  2. 若是CAS操做失敗,說明存在鎖競爭,鎖已經膨脹成重量級鎖,須要在釋放鎖的同時喚醒那些被掛起的線程

3.重量級鎖

重量級鎖依賴於底層操做系統的Mutex Lock,全部線程都會被阻塞住,線程之間的切換須要從用戶態到內核態,切換成本很是高。指針

總結:鎖的優缺點對比

優勢 缺點 適用場景
偏向鎖(Biased Lock) 加鎖和解鎖不須要額外的消耗,和執行非同步方法相比僅存在納秒級的差距 若是線程間存在鎖競爭,會帶來額外的鎖撤銷 適用於只有一個線程訪問
輕量級鎖(Lightweight Lock) 競爭的線程不會阻塞,提升了程序的響應速度 對於得不到鎖的線程,自旋會消耗CPU 追求響應時間,或者要求臨界區簡短,自旋不會佔用CPU太久
重量級鎖(Heavyweight Lock) 線程競爭不使用自旋,不會消耗CPU資源 線程阻塞,響應時間緩慢 追求吞吐量
相關文章
相關標籤/搜索