synchronized鎖原理monitor


monitor(監視器/管程)


java對象分三部分,

  1. 對象頭
  2. 數據實例
  3. 填充

對象頭分為

  1. 普通對象- markword(32bit)/klass word(32bit)(指向對應的class對象)
  2. 數組對象-多一個array length(32bit)數組長度

markword的結構

  1. hashcode(25) age(4) biased_lock:0(代表是否是偏向鎖) | 01 (代表加鎖狀態) normal狀態(正常狀態)
  2. thread:54(線程id) epoch:2 age(4) biased_lock:1(代表是偏向鎖)|01(代表加鎖狀態) biased(偏向鎖)
  3. ptr_to_heavyweight_monitor(30,指向monitor的指針) |10(代表加了重量級 heavyweight lock(重量級鎖定)
  4. ptr_to_lock_record:30 (30位代表鎖記錄的地址) |00(代表加輕鎖) LightWeight Lock(輕量級鎖定)

monitor是操作系統提供的,內部結構

  1. waitset

  2. entrylist(等待隊列)

  3. owner(monitor擁有者)

    `具體步驟1是字節碼中的monitorenter操作指令 步驟4是字節碼中monitorexit指令
    重量級鎖的加鎖流程:
    1. thread0執行synchronize代碼的時候,synchronized(obj)的obj對象的markword中ptr_to_heavyweight_monitor會指向一個monitor對象,monitor的owner是thread0
    2. thread1執行到synchronized代碼時,發現obj的markword指向了一個monitor並且owner有人了,這時會進入entrylist進行blocked
    3. thread2也一樣
    4. thread0執行完同步代碼退出synchronized,把obj markword里的數據還原比如hashcode age啥的,這些數據是存在monitor對象中的,然后喚醒entrylist的thread1和thread2的blocked線程,兩個線程去搶owner
    5. 如果出現異常,jvm會自動釋放鎖 執行第4步

`
因為獲取monitor是系統指令,需要從用戶態轉為內核態,出現上下文切換啥的,比較耗費資源,所以java1.6版本對synchronized進行了優化

  1. 輕量級鎖/使用場景是一個對象雖然有多個線程訪問,但是不出現競爭,這時使用輕量級鎖來優化
    加鎖流程:

    1. 創建鎖記錄(lock record)對象(內部存儲鎖定對象的mark word,還有一個對象指針記錄)
    2. 讓鎖記錄中的object reference指向鎖對象,將鎖對象的mark word值存入鎖記錄(這時鎖記錄記錄的是hashcode age 01無鎖狀態,obj鎖對象對象頭中存儲的是鎖記錄地址和狀態00 鎖添加成功
    3. 如果obj鎖對象中的狀態已經是00鎖記錄存儲的是其他線程這時加鎖失敗
      cas替換失敗了有兩種情況
      1.如果其他線程已經持有了該object的輕量級鎖,這時出現了競爭,進行鎖膨脹
      2.如果是線程自己執行了鎖重入,添加一條lock record在鎖持有線程的棧幀中 指向obj鎖對象 並進行cas操作(一定失敗),作為重入的計數

    釋放鎖流程:

    1. 退出同步代碼塊時,如果有鎖記錄為null的情況表示有重入,重置鎖記錄表示重入計數減一
    2. 當鎖記錄不為null,這時使用cas操作恢復markword給obj鎖對象的對象頭
      兩種情況:
    3. 恢復成功 解鎖成功
    4. 失敗說明輕量級鎖進行了鎖膨脹,這時需要執行重量級鎖的解鎖流程

鎖膨脹:
輕量級鎖出現了競爭(在嘗試加輕量級鎖的過程中,其他線程為此對象已經加上了輕量級鎖),這時需要進行鎖膨脹,升級為重量級鎖
thread0持有着輕量級鎖,thread1進來加輕量級鎖,出現競爭這時thread1加鎖失敗進行鎖膨脹,
1. thread1為obj對象申請monitor對象,並將obj鎖對象的mark word中指向monitor,thread1自己進入entrylist進行blocked
2. 當thread0執行完同步代碼塊,解鎖的時候發現鎖的標識變了,mark word也不是指向棧幀中的鎖記錄了,這時執行重量級鎖的解鎖流程

自旋優化(適用於多核處理器)
重量級鎖出現競爭的時候,使用自旋來優化

偏向鎖(優先加偏向鎖)
輕量級鎖在加鎖成功后,鎖重入的時候會再執行一次cas的操作(雖然必定失敗),這樣會浪費資源
偏向鎖的加鎖流程:
第一次使用cas操作將線程的id存入obj對象頭的markword,之后發現這個id是自己就說明沒有競爭,不用重新cas操作,只要不發生競爭,這個鎖就歸這個線程所有
如果調用對象的hashcode,會撤銷obj鎖對象的偏向狀態
當申請obj對象鎖的線程是不偏向的那個線程的時候(這時沒有競爭),會升級為輕量級鎖
批量重偏向
當偏向鎖撤銷超過20次,重偏向給加鎖的線程
批量撤銷
當偏向鎖撤銷超過40次,整個類的所有對象都變為不可偏向的
鎖消除
jit及時編譯器在運行時會優化掉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM