簡單理解重量級鎖、輕量級鎖、偏向鎖

全文使用synchronized來說明。
synchronized給對象上鎖,先上偏向鎖,在上輕量級鎖,最後上重量級鎖。上什麼鎖,是gvm根據競爭程度自行變換的。

重量級鎖

計算機操作系統本有Monitor對象,稱爲管程。在java裏面看不到此對象。
每個Java對象都可以關聯一個monitor對象,如果使用了synchronized給對象上重量級鎖後,該對象的Mark word就被設置指向monitor對象的指針。

Monitor對象結構

在這裏插入圖片描述

  • WaitSet:是線程等待隊列。狀態爲WAITING
  • EntryList:線程阻塞隊列.狀態爲BLOCKED
  • Owner:正在執行的線程(可能很多線程競爭一個資源,但只有一個線程能夠成功,此時Owner就置爲此線程)

原理

  1. 新建的對象,此時Mark Word關聯一個Monitor對象(即Mark Word記錄一個monitor對象的地址)。此時Monitor對象裏面的Owner爲null.
    因爲還沒有線程去獲得Monitor鎖。
    在這裏插入圖片描述
  2. 多個線程競爭,只有線程1成功。其他進入阻塞隊列。(或者說,只要owner非空,那麼其他線程就要進入阻塞隊列)
    當線程1執行完畢後,通知阻塞隊列裏的線程,引起它們的非公平性競爭。
    在這裏插入圖片描述
  3. 若此時Owner線程調用wait方法,那麼會進入WaitSet。
    當被喚醒時(如調用notify())會進入EntryList重新競爭。

輕量級鎖

如果一個對象雖然有多線程訪問,但多線程訪問的時間是錯開的(或者說沒有競爭)的話,可以使用輕量級鎖來優化。
當有競爭時,會發生鎖膨脹,變爲重量級鎖,

java 對象頭

以32爲虛擬機爲例
普通對象是:
在這裏插入圖片描述

  • Klass Word:是一個指針,通過他可以知道是個啥類對象
  • Mark Word:
    在這裏插入圖片描述
    如 Normal ,即沒有上鎖,末尾兩位是01;輕量級鎖是00
    下面用Hashcode age Bias 01 代替最初的Mark Word

原理

在這裏插入圖片描述
每個線程的棧幀都會包含一個鎖記錄的結構,內部可以保存鎖對象的Mark Word


  • 當線程執行到臨界區代碼時,對obj上鎖。讓鎖記錄中的Object reference 指向鎖對象,並嘗試用cas替換Object的Mark Word,將 Mark Word的值存入鎖記錄。
    • cas替換成功:那麼Object對象就會存儲鎖記錄狀態00和地址 ,表示由該線程給對象加鎖。 當Mark Word末尾是01時,纔可以替換成功
      在這裏插入圖片描述

    • cas替換失敗

      • case 1:其他線程已經持有了改Object的輕量級鎖,表明有競爭,進入鎖膨脹過程
      • case 2:自己執行synchronized鎖重入,再添加一條Lock Record作爲重入的計數
        在這裏插入圖片描述
        此時進行cas操作自然會失敗。
        最後再講鎖膨脹。
    • 解鎖

      • 當Lock Record 記錄裏面存在null,說明存在重進入,這時重置鎖記錄,表示重進入計數減一
      • 鎖記錄不爲空,這時用cas操作把鎖記錄裏面保存的Mark Word替換給對象頭
        • 成功則解鎖成功
        • 失敗說明進行了鎖膨脹或已經升級爲重量級鎖,進入重量級鎖的解鎖流程。

鎖膨脹

在嘗試加輕量級鎖的過程中,CAS操作無法成功,一個原因就是有其他線程爲此對象加上了輕量級鎖,這時需要進行鎖膨脹,將輕量級鎖變爲重量級鎖。
如線程1持有object的鎖,這時線程2也想要,但進行cas操作失敗,這時候會發生:

  • 爲object對象申請Monitor鎖,讓object指向重量級鎖地址(此時後兩位爲10,即重量級鎖)
  • 然後線程1進入Monitor的EntryList阻塞
    在這裏插入圖片描述
    當線程1執行完後,想要退出臨界區,使用cas將Mark Word的值替換給對象頭,但是會失敗。因爲object的Mark Word後兩位變爲10,已經不再是00了。
    這時按照Monitor地址找到monitor對象,設置Owner爲null,再喚醒線程2.

偏向鎖

如果只有一個線程,它多次重進入,那會多次建立空的鎖記錄,作爲鎖重入的計數。
因此,java 6中引入了偏向鎖來做進一步優化:只有第一次使用CAS將線程ID設置到對象的Mark Word,之後只要發現這個線程ID是自己的就表示沒有發生競爭,不用重新CAS。以後只要不發生競爭,這個對象就歸該線程所有。
當線程請求到鎖對象後,將鎖對象的狀態標誌位改爲01,即偏向模式。然後使用CAS操作將線程的ID記錄在鎖對象的Mark Word中。以後該線程可以直接進入同步塊,連CAS操作都不需要。但是,一旦有第二條線程需要競爭鎖,那麼偏向模式立即結束,進入輕量級鎖的狀態。
在這裏插入圖片描述

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章