阿里的人問什麼是鎖膨脹,答不上來,回來作了總結:html
關於鎖的膨脹,synchronized的原理參考:深刻分析Synchronized原理(阿里面試題)面試
首先說一下鎖的優化策略。編程
1,自旋鎖併發
自旋鎖其實就是在拿鎖時發現已經有線程拿了鎖,本身若是去拿會阻塞本身,這個時候會選擇進行一次忙循環嘗試。也就是不停循環看是否能等到上個線程本身釋放鎖。這個問題是基於一個現實考量的:不少拿了鎖的線程會很快釋放鎖。由於通常敏感的操做不會不少。固然這個是一個不能徹底肯定的狀況,只能說整體上是一種優化。post
舉個例子就比如一我的要上廁所發現廁所裏面有人,他能夠:1,等一小會。2,跑去另外的地方上廁所。等一小會不必定能等到前一我的出來,不過若是跑去別的廁所的花費的時間確定比等一小會結果前一我的出來了長。固然等完告終果那我的沒出來仍是要跑去別的地方上廁所這是最慢的。優化
而後是基於這種作法的一個優化:自適應自旋鎖。也就是說,第一次設置最多自旋10次,結果在自旋的過程當中成功得到了鎖,那麼下一次就能夠設置成最多自旋20次。道理是:一個鎖若是可以在自旋的過程當中被釋放說明頗有可能下一次也會發生這種事。那麼就更要給這個鎖某種「便利」方便其不阻塞得鎖(畢竟快了不少)。一樣若是屢次嘗試的結果是徹底不能自旋等到其釋放鎖,那麼就說明頗有可能這個臨界區裏面的操做比較耗時間。就減少自旋的次數,由於其可能性過小了。url
2,鎖粗化spa
試想有一個循環,循環裏面是一些敏感操做,有的人就在循環裏面寫上了synchronized關鍵字。這樣確實沒錯不過效率也許會很低,由於其頻繁地拿鎖釋放鎖。要知道鎖的取得(假如只考慮重量級MutexLock)是須要操做系統調用的,從用戶態進入內核態,開銷很大(阿里面試)。因而針對這種狀況也許虛擬機發現了以後會適當擴大加鎖的範圍(因此叫鎖粗化)以免頻繁的拿鎖釋放鎖的過程。操作系統
3,鎖消除線程
經過逃逸分析發現其實根本就沒有別的線程產生競爭的可能(別的線程沒有臨界量的引用),而「自做多情」地給本身加上了鎖。有可能虛擬機會直接去掉這個鎖。
4,偏向鎖和輕量級鎖
這兩個鎖既是一種優化策略,也是一種膨脹過程因此一塊兒說。首先它們的關係是:最高效的是偏向鎖,儘可能使用偏向鎖,若是不能(發生了競爭)就膨脹爲輕量級鎖,這樣優化的效率不如原來高不過仍是一種優化(對比重量級鎖而言)。因此整個過程是儘量地優化。
首先說說偏向鎖。
HotSpot的研究人員發現大多數狀況下雖然加了鎖,可是沒有競爭的發生,甚至是同一個線程反覆得到這個鎖。那麼偏向鎖就爲了針對這種狀況。
舉個例子,一個倉庫管理員管着鑰匙,然而每一次都是老王去借,倉庫管理員因而就認識了老王,直接和他說,「行,你直接拿就是不用填表格了我記得你」。
講一下偏向鎖的具體過程。首先JVM要設置爲可用偏向鎖。而後當一個進程訪問同步塊而且得到鎖的時候,會在對象頭和棧幀的鎖記錄裏面存儲取得偏向鎖的線程ID。
下一次有線程嘗試獲取鎖的時候,首先檢查這個對象頭的MarkWord是否是儲存着這個線程的ID。若是是,那麼直接進去而不須要任何別的操做。若是不是,那麼分爲兩種狀況。1,對象的偏向鎖標誌位爲0(當前不是偏向鎖),說明發生了競爭,已經膨脹爲輕量級鎖,這時使用CAS操做嘗試得到鎖(這個操做具體是輕量級鎖的得到鎖的過程下面講)。2,偏向鎖標誌位爲1,說明仍是偏向鎖不過請求的線程不是原來那個了。這時只須要使用CAS嘗試把對象頭偏向鎖從原來那個線程指向目前求鎖的線程。這種狀況舉個例子就是老王準備退休了,他兒子接替他來拿鑰匙,因而倉庫管理員認識了他兒子,他兒子每次來也不用登記註冊了。
這個CAS失敗了呢?首先必須明確這個CAS爲何會失敗,也就是說發生了競爭,有別的線程和它搶鎖而且搶贏了,那麼這個狀況下,它就會要求撤銷偏向鎖(由於發生了競爭)。接着它首先暫停擁有偏向鎖的線程,檢查這個線程是不是個活動線程,若是不是,那麼好,你拿了鎖可是沒在幹事,鎖還記錄着你,那麼直接把對象頭設置爲無鎖狀態從新來過。若是仍是活動線程,先遍歷棧幀裏面的鎖記錄,讓這個偏向鎖變爲無鎖狀態,而後恢復線程。
再說輕量級鎖。這是偏向鎖膨脹以後的產物。
加鎖的過程:JVM在當前線程的棧幀中建立用於儲存鎖記錄的空間(LockRecord),而後把MarkWord放進去,同時生成一個叫Owner的指針指向那個加鎖的對象,同時用CAS嘗試把對象頭的MarkWord成一個指向鎖記錄的指針。至於synchronized的原理 也是阿里問的熱點問題
成功了就拿到了鎖。那麼失敗了呢?失敗了的說法比較多。主流有《深刻理解JVM》的說法和《併發編程的藝術》的說法。
《深刻理解JVM》的說法:
失敗了,去查看MarkWord的值。有2種可能:1,指向當前線程的指針,2,別的值。
若是是1,那麼說明發生了「重入」的狀況,直接當作成功得到鎖處理。
其實這個有個疑問,爲何得到鎖成功了而CAS失敗了,這裏其實要牽扯到CAS的具體過程:先比較某個值是否是預測的值,是的話就動用原子操做交換(或賦值),不然不操做直接返回失敗。在用CAS的時候期待的值是其本來的MarkWord。發生「重入」的時候會發現其值不是期待的本來的MarkWord,而是一個指針,因此固然就返回失敗,可是若是這個指針指向這個線程,那麼說明其實已經得到了鎖,不過是再進入一次。若是不是這個線程,那麼狀況2:
若是是2,那麼發生了競爭,鎖會膨脹爲一個重量級鎖(MutexLock)
《併發編程的藝術》的說法:
失敗了直接自旋。指望在自旋的時間內得到鎖,若是仍是不能得到,那麼開始膨脹,修改鎖的MarkWord改成重量級鎖的指針,而且阻塞本身。
解鎖過程:(那個拿到鎖的線程)用CAS把MarkWord換回到原來的對象頭,若是成功,那麼沒有競爭發生,解鎖完成。若是失敗,表示存在競爭(以前有線程試圖經過CAS修改MarkWord),這時要釋放鎖而且喚醒阻塞的線程。