前文講解輕量級鎖時,當線程使用CAS嘗試對對象加鎖,有兩種失敗情況,一種情況是自己執行了synchronized鎖重入;另外一種,就是本文需要學習的重點內容:鎖膨脹。
一、何爲鎖膨脹?
輕量級鎖是指在滿足一定的條件內,使用CAS(自旋)來嘗試獲取對象鎖的一種機制,如果超過以下條件,則會膨脹爲重量級鎖:
1)在jdk1.6前,默認10次,可通過-XX:PreBlockSpin來修改,或者自旋線程數超過CPU核數的一半。
2)jdk1.6之後,引入了自適應自旋鎖,次數並非一成不變。根據獲取鎖的成功率來決定是否能有更長的等待時間。
假設當前Object對象,已經被Thread1所持有,當Thread2前來競爭這把鎖,滿足上述條件後,會發生鎖膨脹,如下圖所示:
如上所示,此時Thread2來獲取輕量級鎖肯定失敗的,所以會進入鎖膨脹的流程:
1)爲Object對象申請Monitor鎖,Object的Mark Word指向Monitor地址;Monitor的Owner指向Thread1的鎖記錄。
2)Thread2進入Monitor的EntryList當中,狀態變成BLOCKED。
當Thread1執行完代碼塊的內容後,開始釋放鎖,使用CAS去重置Object的Mark Word,此時會失敗。因爲當前對象頭存儲的是Monitor的地址。
所示此時會進入重量級鎖的解鎖過程。將Monitor的Owner設置爲null,同時喚醒EntryList中的Thread-2。
二、自適應自旋鎖
jdk1.6之後,引入了自適應自旋鎖,在重量級鎖當中,也進行了一些優化。
前面提到當發生鎖膨脹後,沒持有鎖的線程會進入Monitor的EntryList當中進行阻塞,實際情況是,會通過自適應自旋鎖進行一定次數的自旋,如果獲取到鎖了,就避免進入阻塞狀態(會進行上下文切換)。如果沒獲取到鎖,此時在進入阻塞狀態。
- 自旋是佔用CPU的,只有在多核CPU中才能發揮優勢。
- 自適應自旋鎖會動態調整自旋次數,獲取鎖成功的次數多,就會多自旋幾次;如果一次都沒有成功,則會可能會直接進行阻塞。
- java7後不能控制是否開啓自旋鎖。