筆記-深入瞭解JVM(鎖優化)

鎖優化

高效併發是JDK1.5-1.6的一個重要改進,hotspot虛擬機開發團隊在這個版本上花費來大量的精力去實現了各種鎖優化技術,

1、自旋鎖與自適應自旋

互斥同步對性能最大的影響是阻塞的實現,掛起和恢復線程的操作都需要轉入內核態中完成,這些操作給系統帶來了很大的壓力。

如果物理機器有一個以上的處理器,能讓兩個或以上的線程同時並行執行,我們就可以讓後面請求鎖的那個線程“稍等一下”,但不放棄處理器的執行時間,看看持有鎖的進程是否很快就會釋放鎖。爲了讓線程等待,我們只需要讓線程執行一個忙等待(自旋),這項技術就是所謂的自旋鎖

自旋等待不能代替阻塞。且不說對處理機數量的要求,本身雖然避免了線程切換的開銷,但它是要佔用處理器時間的。

所以自旋等待時間必須有一定的限制,如果自旋超過了限定的次數仍然沒有成功獲得鎖資源,就應當使用傳統的方式去掛起線程了。

JDK1.6引進了自適應的自旋鎖。自適應意味着自旋的時間不再固定了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定。有了自適應自旋,隨着程序運行和性能監控信息的不斷完善,虛擬機對程序鎖的狀態預測就會越來越準確,虛擬機就會越來越“聰明”了。

2、鎖消除

鎖消除是指虛擬機即時編譯器在運行時,對一些代碼上要求同步,但是被檢測到不可能存在共享數據競爭的鎖進行消除。

鎖消除的只要判定依據來源於逃逸分析的數據支持,如果一段代碼中,堆上的所有數據都不會逃逸出去從而被其他線程訪問到,那就可以把它們當做棧上數據對待,認爲它們是私有的,同步加鎖自然就無須進行。,沒有必要。

許多同步措施並不是程序員自己加入的,同步的代碼在Java程序中的普遍程度也許超過了大部分讀者的想象。

3、鎖粗化

如果虛擬機探測到有這樣一串零碎的操作都對同一個對象加鎖,將會把加鎖同步的範圍擴展(粗化)到整個操作序列的外部。即三個String序列相加,而產生一個新String對象承接這個相加後的String對象,在這個過程中,javac程序會自動做優化,而將“+”改成StringBuffer的append方法依次附加到sb字符序列後面,這時有三個連續的append操作,若每個方法都進行一次加鎖和解鎖的過程,那麼即使沒有線程競爭,頻繁地進行互斥同步操作也會導致不必要的性能消耗。

4、輕量級鎖

傳統的鎖機制稱爲“重量級”鎖。他的本意是,在沒有多線程競爭的前提下,減少傳統的重量級鎖使用操作系統互斥量產生的性能消耗。

HotSpot虛擬機的對象頭分爲兩部分信息,第一部分用於存儲對象自身的運行時數據,如哈希碼,GC分代年齡等,這部分數據的長度在32位和64位的虛擬機中分別爲32bit和64bit,它是實現輕量級鎖和偏向鎖的關鍵另外一部分用於存儲指向方法區對象類型數據的指針,如果數組對象的話,還會有一個額外的部分用於存儲數組長度。

輕量級鎖執行過程

在代碼進入同步塊的時候,如果此同步對象沒有被鎖定(鎖標誌位01狀態),虛擬機首先將在當前線程的棧幀中建立一個名爲鎖記錄的空間,用於存儲鎖對象目前的Mark Word的拷貝。

然後,虛擬機將使用CAS操作嘗試將對象的Mark Word更新爲指向Lock Record的指針。

如果更新成功,那麼這個線程就獲得了對象的鎖,並且對象Mark Word的鎖標誌位(變爲00),即表示此對象處於輕量級鎖定狀態。

更新失敗,虛擬機首先會檢查對象的Mark Word是否指向當前線程的棧幀,如果只說明當前線程已經擁有了這個對象的鎖,那就可以直接進入同步塊繼續執行,否則說明這個鎖對象已經被其他線程搶佔了。

如果有兩個以上的線程爭用同一個鎖,那輕量級鎖就不再有效,再膨脹爲重量級鎖,鎖標誌狀態值變爲10

Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,後面等待鎖的線程也要進入阻塞狀態。

解鎖過程也是通過CAS操作來進行的,如果對象的Mark Word仍然指向着線程的鎖記錄,那就用CAS操作把對象當前的Mark Word和線程中複製的Displaced Mark Word替換回來,如果替換成功,整個同步過程就完成了。

5、偏向鎖

目的消除數據在無競爭情況下的同步原語,進一步提高程序的運行性能。如果說輕量級鎖是在無競爭情況下使用CAS操作去消除同步使用的互斥量,那偏向鎖就是在無競爭的情況下把整個同步都消除掉,連CAS都不做。

偏向鎖的“偏”,意思就是鎖會偏向於第一個獲得它的線程,如果在接下來的執行過程中,該鎖沒有被其他的線程獲取,則持有偏向鎖的線程將永遠不需要再進行同步。

對象頭的標誌位01,是偏向模式

00輕量級鎖

偏向鎖可以提高帶有同步但無競爭的程序性能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章