1. 程式人生 > >JVM學習篇(4)之執行緒安全與鎖優化

JVM學習篇(4)之執行緒安全與鎖優化

執行緒安全與鎖優化

Java中執行緒安全

對共享資料的操作

1.        不可變:

不可變的物件一定是執行緒安全的。如String類。

2.        絕對執行緒安全

3.        相對執行緒安全

4.        執行緒相容

5.        執行緒對立

執行緒安全的實現方法

1.        互斥同步(阻塞式同步)

1)        同步指的是:多個執行緒併發訪問共享資料時,保證共享資料在同一時刻只能被一個執行緒使用。

2)        互斥指的是:同步的手段。如:臨界區、互斥量和訊號量。

3)        最基本的同步互斥手段:synchronized關鍵字。

a)        原理:synchronized關鍵字經過編譯後,會在同步程式碼塊前後分別形成monitorenter和monitorexit這兩個位元組碼指令。

b)        在執行monitorenter指令時,首先要嘗試獲取物件鎖。若這個物件沒被設定鎖,或已經擁有了這個物件鎖,把鎖的計數器+1。

c)        相應的執行monitorexit指令時,鎖計數器-1,當為0時釋放鎖。

d)        若獲得物件失敗則阻塞當前執行緒。

4)        另一種手段:ReentrantLock來實現

reentrantLock增加的新功能:

a)        等待中斷:當持有鎖的執行緒長期不釋放鎖的時候,正在等待的執行緒可以選擇放棄等待。

b)        公平鎖:可以講多個執行緒在等待同一個鎖時,必須按照申請鎖的時間順序依次獲得鎖。

c)        鎖繫結多個條件:一個ReentranLock物件可以同時繫結多個Condition物件。

5)        兩種方式的比較:

a)        在JDK1.6之前在多執行緒環境中synchronized的吞吐量下降的嚴重,但ReentranLock效能幾乎不變。

b)        在JDK1.6之後二者效能基本上持平。

c)        應該優先考慮使用synchronized來同步。

2.        非阻塞同步

1)        互斥同步屬於一種悲觀的併發策略。即總認為不去做同步措施就一定會出現問題,故無論共享資料是否真的會出現競爭,都要加鎖。

2)        非執行緒阻塞:是基於衝突檢測的樂觀併發控制策略。即先操作,如果沒有其它執行緒徵用共享資料,則成功。如果共享資料有徵用,採取補償措施。(不斷的重試,直到成功)

3.        無同步方案

如果方法中不涉及共享資料,那它自然無須任何同步措施去保證正確性。

1)        可重入程式碼

可重入的程式碼都是執行緒安全的,但並非所有的執行緒安全的程式碼都是可重入的。

2)        執行緒本地儲存

鎖優化

鎖優化技術:適應性自旋、鎖消除、鎖粗化、輕量級鎖和偏向鎖。

適應性自旋

1.        自旋鎖:當兩個或兩個以上的執行緒同時並行執行,讓後面請求鎖的那個執行緒“稍等一下”,但不放棄處理器的執行時間,看看持有所的執行緒是否能很快釋放鎖。

2.        缺點:自旋的執行緒只會白白消耗處理器資源,帶來效能上的浪費。故需要使等待時間有一定的限度

3.        改進:等待時間要自適應。等待時間隨著程式執行和效能監控資訊的不斷完善。

鎖消除

1.        鎖消除:虛擬機器及時編譯器在執行時,對一些程式碼上要求同步,但是被檢測到不可能存在共享資料競爭的鎖進行消除。

2.        判斷依據:源於逃逸分析的資料支援。若堆上的所有資料都不會逃逸出去從而被其他執行緒訪問到,那就可以吧它們當作棧上的資料看待。

粗粒化

1.        上面出現的問題:如果一系列的連續操作都對同一個物件反覆加鎖和解鎖,甚至加鎖操作是出現在迴圈體中,那即使沒有執行緒競爭,頻繁地進行互斥同步操作也會導致不必要的效能損耗。

2.        措施:如果虛擬機器探測到有這樣一串零碎的操作都對同一個物件加鎖,【將會把加鎖同步的範圍擴充套件到整個操作序列外部】。

輕量級鎖

1.        加鎖過程:

1)        程式碼進入到同步程式碼塊的時候,如果此同步物件沒有被鎖定,將會在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)空間,用來儲存物件目前的頭資料。

2)        將物件的頭記錄更新為指向Lock Record的指標。

a)        更新成功:那麼這個執行緒就擁有了該物件的鎖

b)        失敗:檢查物件的物件頭是否指向當前執行緒的棧幀。

a)        是:說明當前執行緒已經擁有這個物件的鎖,直接進入同步塊中執行。

b)        否:說明這個鎖物件已經被其他執行緒搶佔。

3)        如果兩條以上執行緒徵用同一個鎖,那輕量級鎖失效。

2.        解鎖過程:

1)        若物件的物件頭仍然指向現成的鎖記錄,則把物件當前的Mark Word和執行緒中副本替換回來。

a)        成功:整個同步過程完成

b)        失敗:說明其他執行緒嘗試獲取該鎖,那就要釋放鎖的同時喚醒被掛起的執行緒。

偏向鎖

1.        與輕量級鎖的區別:

輕量級鎖:是在無競爭情況下,消除同步使用的互斥量。

偏向鎖:在無競爭情況下把整個同步都消除掉。

2.        加鎖過程:

1)        當鎖物件第一次被執行緒獲取的時候,虛擬機器包物件頭中的標記為設為“01”。

2)        把獲取到這個鎖的執行緒的ID記錄在物件的Mark Word之中。

a)        成功:持有偏向鎖的執行緒以後每次進入這個鎖相關的同步塊時,可不再進行任何同步操作。

3.        解鎖過程:

1)        當有另外一個執行緒去嘗試獲取這個鎖時,偏向模式宣告結束。

2)        根據鎖物件目前是否處於被鎖定的狀態,撤銷偏向後恢復到“01”或“00”。

4.        優點:

提高帶有同步但競爭的程式效能。