java對象頭與synchronized鎖的升級過程
java對象頭中都存了些什么?
32位jdk中:
| 鎖狀態 | 25bit | 4bit | 1bit | 2bit | |
| 23bit | 2bit | 偏向鎖標志位(biased_lock) | 鎖狀態(lock) | ||
| 正常對象(normal object) | 對象hashcode(hash) | 對象分代年齡(age) | 0 | 01 | |
| 偏向鎖(biased object) | 線程ID | Epoch | 對象分代年齡(age) | 1 | 01 |
| 輕量級鎖 | 指向棧中鎖記錄指針 | 00 | |||
| 重量級鎖 | 指向互斥量(重量級鎖)的指針 | 10 | |||
| GC標記 | 空 | 11 | |||
64位jdk中:
| 鎖狀態 | 25位 | 31位 | 1位 | 4位 | 1位 | 2位 | |
| 29位 | 2位 | 偏向鎖標志位(biased_lock) | 鎖狀態(lock | ||||
| 正常對象(normal object) | 未使用 | 對象hashcode(hash) | 未使用 | 對象分代年齡(age) | 0 | 01 | |
| 偏向鎖(biased object) | 線程ID | Epoch | 未使用 | 對象分代年齡(age) | 1 | 01 | |
在javaSE1.6中,為了減少上下文切換帶來的性能消耗,jdk引入了偏向鎖與輕量鎖
synchronized鎖保證線程安全時鎖的升級過程:
偏向鎖:由于重量級鎖在每次釋放與獲取鎖時進行上下文切換對性能消耗大,而多數鎖的獲取與釋放常常在同一個線程中進行,針對該現象引入了偏向鎖進行優化;當一個線程訪問同步代碼塊并獲取鎖時,會在對象頭和棧幀中的所記錄里存儲偏向鎖的線程ID,以后該線程在進入和退出同步代碼塊時,不用進行CAS操作來加鎖解鎖,只需要測試對象頭的Mark World中是否存儲著指向當前線程的偏向鎖,如果測試成功,表示該線程已經獲取到鎖進行執行,如果測試失敗,則需要再次測試Mark world中偏向鎖標識位是否為1(表示位偏向鎖),如果沒有設置,則開始使用輕量級鎖(CAS)進行競爭鎖,如果是,則嘗試使用CAS將對象頭的偏向鎖指向當前線程
偏向鎖撤銷:當有另一個線程進行競爭(競爭出現)這把鎖時,才會撤銷偏向鎖,撤銷的過程要等到全局安全點(在這個時間點上沒有代碼正在執行)才會進行,撤銷前要西安暫停擁有偏向鎖的線程,再檢查該線程是否存活,如果不存活,則將對象頭設位無所,否則擁有偏向鎖的棧會被執行,遍歷偏向對象的鎖記錄,棧中所記錄和對象頭的mark world要么偏向于其他線程,要么恢復到無鎖或者標記對象不適合作為偏向鎖,最后喚醒暫停的線程
偏向鎖會在程序啟動幾秒后激活:可以通過-XX:BiasedLockingStartupDelay=0來消除這個延時
如果線程中的鎖通常處于競爭狀態,可以通過-XX:-UseBiasedKocking=false來關閉偏向鎖

輕量級鎖:
加鎖:線程執行同步代碼塊前,jvm再當前線程的棧幀中創建用于存儲所記錄的空間,并將對象頭中的mark word復制到所記錄中,官方稱為displaced mark word。然后線程嘗試使用CAS將對象頭的mark word替換為指向鎖記錄的指針。如果成功當前線程獲取鎖,否則其他線程獲得鎖,當前線程CAS等待
解鎖:輕量級鎖解鎖時,會使用原子的CAS操作將Displaced mark word替換回對象頭,如果成功,則表示沒有競爭,如果失敗,則表示該鎖當前存在競爭,鎖會升級為重量級鎖

重量級鎖:為了避免無用的自旋,一當鎖升級為重量級鎖,就不會恢復到輕量級鎖,當鎖為重量級鎖的狀態下,其他線程試圖獲取鎖時,都將處于阻塞狀態,當持有鎖的線程釋放鎖時,會喚醒其他線程,進行新一輪鎖的競爭

浙公網安備 33010602011771號