JVM鎖優化以及區別

偏向所鎖,輕量級鎖都是樂觀鎖,重量級鎖是悲觀鎖。 java

首先簡單說下先偏向鎖、輕量級鎖、重量級鎖三者各自的應用場景:優化

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

還要明確的是,偏向鎖、輕量級鎖都是JVM引入的鎖優化手段,目的是下降線程同步的開銷。好比如下的同步代碼塊:spa

synchronized (lockObject) { // do something }

上述同步代碼塊中存在一個臨界區,假設當前存在Thread#1和Thread#2這兩個用戶線程,分三種狀況來討論:線程

  • 狀況一:只有Thread#1會進入臨界區;
  • 狀況二:Thread#1和Thread#2交替進入臨界區;
  • 狀況三:Thread#1和Thread#2同時進入臨界區。

sychronized鎖優化解釋1:指針

上述的狀況一是偏向鎖的適用場景,此時當Thread#1進入臨界區時,JVM會將lockObject的對象頭Mark Word的鎖標誌位設爲「01」,同時會用CAS操做把Thread#1的線程ID記錄到Mark Word中,此時進入偏向模式。所謂「偏向」,指的是這個鎖會偏向於Thread#1,若接下來沒有其餘線程進入臨界區,則Thread#1再出入臨界區無需再執行任何同步操做。也就是說,若只有Thread#1會進入臨界區,實際上只有Thread#1初次進入臨界區時須要執行CAS操做,之後再出入臨界區都不會有同步操做帶來的開銷。code

然而狀況一是一個比較理想的狀況,更多時候Thread#2也會嘗試進入臨界區。若Thread#2嘗試進入時Thread#1已退出臨界區,即此時lockObject處於未鎖定狀態,這時說明偏向鎖上發生了競爭(對應狀況二),此時會撤銷偏向,Mark Word中再也不存放偏向線程ID,而是存放hashCode和GC分代年齡,同時鎖標識位變爲「01」(表示未鎖定),這時Thread#2會獲取lockObject的輕量級鎖。由於此時Thread#1和Thread#2交替進入臨界區,因此偏向鎖沒法知足需求,須要膨脹到輕量級鎖。對象

再說輕量級鎖何時會膨脹到重量級鎖。若一直是Thread#1和Thread#2交替進入臨界區,那麼沒有問題,輕量鎖hold住。一旦在輕量級鎖上發生競爭,即出現「Thread#1和Thread#2同時進入臨界區」的狀況,輕量級鎖就hold不住了。 (根本緣由是輕量級鎖沒有足夠的空間存儲額外狀態,此時若不膨脹爲重量級鎖,則全部等待輕量鎖的線程只能自旋,可能會損失不少CPU時間)資源

 

sychronized鎖優化解釋2:同步

一個對象剛開始實例化的時候,沒有任何線程來訪問它的時候。它是可偏向的,意味着,它如今認爲只可能有一個線程來訪問它,因此當第一個線程來訪問它的時候,它會偏向這個線程,此時,對象持有偏向鎖。偏向第一個線程,這個線程在修改對象頭成爲偏向鎖的時候使用CAS操做,並將對象頭中的ThreadID改爲本身的ID,以後再次訪問這個對象時,只須要對比ID,不須要再使用CAS在進行操做。hash

一旦有第二個線程訪問這個對象,由於偏向鎖不會主動釋放,因此第二個線程能夠看到對象時偏向狀態,這時代表在這個對象上已經存在競爭了,檢查原來持有該對象鎖的線程是否依然存活,若是掛了,則能夠將對象變爲無鎖狀態,而後從新偏向新的線程,若是原來的線程依然存活,則立刻執行那個線程的操做棧,檢查該對象的使用狀況,若是仍然須要持有偏向鎖,則偏向鎖升級爲輕量級鎖,(偏向鎖就是這個時候升級爲輕量級鎖的)。若是不存在使用了,則能夠將對象回覆成無鎖狀態,而後從新偏向。     

輕量級鎖認爲競爭存在,可是競爭的程度很輕,通常兩個線程對於同一個鎖的操做都會錯開,或者說稍微等待一下(自旋),另外一個線程就會釋放鎖。 可是當自旋超過必定的次數,或者一個線程在持有鎖,一個在自旋,又有第三個來訪時,輕量級鎖膨脹爲重量級鎖,重量級鎖使除了擁有鎖的線程之外的線程都阻塞,防止CPU空轉。

 

 補充:輕量鎖膨脹到重量鎖有兩個條件

1.等待的線程自旋超過必定次數。

2.持有鎖的線程CAS釋放鎖,可是失敗。

解釋:

1.很顯然,自旋超過必定次數會消耗CPU資源,乾脆阻塞。

2.當線程CAS釋放鎖的時候,比較對象頭中的mark word是否指向本線程的lock record

(此處認爲還要比較對象頭中markword是否和線程lock record中的displace mark word相同,我認爲是沒道理的,由於此時lock record中的displaced存儲的是對象的hashcode,而對象的mark word中存儲的倒是指向lock record的指針,所以不可能相同。並且此處的CAS指得就是用hashcode替換指針)。當發現CAS失敗的時候,說明什麼?儘管有一個線程在自旋,可是仍是CAS失敗,說明競爭比較頻繁,所以升級成重量鎖,阻塞其餘線程,本身安安穩穩的釋放掉後再喚醒其餘線程,而後讓其餘線程競爭去。
相關文章
相關標籤/搜索