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. 優點:
提高帶有同步但競爭的程式效能。