天天看點

synchronized鎖更新_更新synchronized鎖,提高效率!

synchronized鎖更新_更新synchronized鎖,提高效率!

前言

synchronized是Java内置的機制,是JVM層面的,而Lock則是接口,是JDK層面的,盡管最初synchronized的性能效率比較差,但是随着版本的更新,synchronized已經變得原來越強大了,synchronized效率很低,因為底層操作依賴于作業系統,作業系統切換線程要從使用者态切換到核心态,花費很多時間。Java SE1.6為了減少獲得鎖和釋放鎖帶來的性能消耗引入了偏向鎖和輕量級鎖

下面解析一下synchronized鎖更新過程和原理,希望可以幫助到大家!

什麼是鎖更新

鎖一共有四種狀态,級别由低到高依次是:無鎖狀态、偏向鎖狀态、輕量級鎖狀态和重量級鎖狀态。這幾個狀态會随着競争情況逐漸更新,但是鎖可以更新不能降級。一個鎖對象剛建立的時候,沒有線程調用它,此時處于無鎖狀态。

對象頭

了解鎖之前,要先了解對象頭,因為鎖的實作是依賴對象頭中的Mark Word的。根據鎖的不同狀态,Mark Word的變化如下:

synchronized鎖更新_更新synchronized鎖,提高效率!

其中無鎖狀态的時候,鎖标志位是01,是否是偏向鎖是0

偏向鎖

偏向鎖的含義

大多數情況下,鎖不僅不存在多線程競争,而是總由同一個線程多次獲得,為了讓線程獲得鎖的代價更低引入了偏向鎖。使用偏向鎖就表明這個鎖對象隻被一個線程調用,可以減少上下文切換。

一個線程獲得了偏向鎖,再次通路鎖對象的時候不需要進行加鎖解鎖的操作,隻需要判斷Mark Word裡面的Thread ID是否是自己。

偏向鎖的實作

當一個線程通路同步塊擷取鎖的時候,先看鎖的标志位是不是01(無鎖或者偏向鎖),在看偏向鎖是不是1。是偏向鎖就判斷Thread ID是不是指向自己,是就進入同步代碼塊,不是就用CAS替換對象頭;無鎖狀态直接CAS替換對象頭。此外,CAS替換對象頭(已偏向)時,先将對象變成無鎖狀态,然後偏向自己

synchronized鎖更新_更新synchronized鎖,提高效率!
  • 什麼時候CAS替換對象頭成功?什麼時候失敗?

    偏向鎖的釋放不是主動的,持有偏向鎖的線程執行完同步代碼後不會主動釋放鎖,要等待其他線程來競争才會釋放鎖。如果線程A持有偏向鎖,但是已經執行完了同步代碼,此時線程B來擷取偏向鎖,CAS就會成功。當線程A并未執行完同步代碼,會出現鎖競争,CAS失敗,偏向鎖更新為輕量級鎖。

    更新為輕量級鎖後鎖标志位00,原持有偏向鎖的線程持有輕量級鎖。

輕量級鎖

在多線程競争不激烈的前提下使用輕量級鎖,可以減少重量級鎖對線程的阻塞帶來的開銷。其他線程在競争鎖的時候隻需要稍微等待(自旋),就可以擷取鎖。但是自旋次數有限,超過自旋次數限制就會更新為重量級鎖。

實作

執行同步代碼塊的時候,JVM會先在目前線程的棧幀中建立存儲鎖記錄的空間(Lock Record),并将對象頭的Mard Word複制到鎖記錄中(Displaced Mark Word),加鎖時使用CAS将對象頭的Mark Word替換為指向鎖記錄的指針,解鎖時使用原子的CAS操作将Displaced Mark Word替換回對象頭。

在擷取鎖失敗時,表示出現鎖競争,該線程嘗試使用自旋來擷取鎖。

synchronized鎖更新_更新synchronized鎖,提高效率!

當自旋超過次數限制或者一個線程持有鎖,一個線程在自旋,又來額外的線程嘗試擷取鎖,此時輕量級鎖會膨脹成重量級鎖

鎖更新過程

  • 偏向鎖:一個線程進入臨界區
  • 輕量級鎖:讀個線程交替進入臨界區
  • 重量級鎖:多個線程同時進入臨界區

    鎖更新過程:

synchronized鎖更新_更新synchronized鎖,提高效率!