Java 虛擬機器對鎖優化所做的努力
作為一款公用平臺,JDK 本身也為併發程式的效能絞盡腦汁,在 JDK 內部也想盡一切辦法提供併發時的系統吞吐量。這裡,我將向大家簡單介紹幾種 JDK 內部的 "鎖" 優化策略。
1、 鎖偏向
鎖偏向是一種針對加鎖操作的優化手段。
如果一個執行緒獲得了鎖,那麼鎖就進入偏向模式。當這個執行緒再次請求鎖時,無須再做任何同步操作。這樣就節省了大量有關鎖申請的操作,從而提高了程式效能。
因此,對於幾乎沒有鎖競爭的場合,偏向鎖有比較紅啊的優化效果,因為連續多次極有可能是同一個執行緒請求相同的鎖。而對於鎖競爭比較激烈的場合,其效果不佳。因為在競爭激烈的場合,最有可能的情況是每次都是不同的執行緒來請求相同的鎖。點選
2、 輕量級鎖
如果偏向鎖失敗,即上一個請求的鎖的執行緒和這個執行緒不是同一個。偏向鎖失敗意味者不能避免做同步操作。此時,虛擬機器並不會立即掛起執行緒。他會使用一種成為輕量級鎖的優化手段。
輕量級鎖的操作也很方便,它只是簡單地將物件頭部作為指標,指向蚩尤鎖的執行緒堆疊的內部,來判斷一個執行緒是否持有物件鎖。 如果執行緒獲得輕量級鎖成功,則可以順利進入臨界區。如果輕量級鎖失敗,則表示其他執行緒搶先爭奪了鎖,那麼當前執行緒的鎖請求就會膨脹為重量級鎖。點選這裡瞭解幾種常見的鎖。
3、 自選鎖
鎖膨脹後,虛擬機器為了避免執行緒真實地在作業系統層面掛起,虛擬機器還會在做最後的努力–自選鎖。
因此虛擬機器讓當前執行緒做個空迴圈,在經過若干次迴圈後,如果可以得到鎖,那麼就順利進入臨界區。如果還不能得到鎖,才會真實地將執行緒在作業系統層面掛起。
4、 鎖消除
鎖消除是一種更徹底的鎖優化。Java虛擬機器在JIT編譯時,通過對執行上下文的掃描,去除不可能存在共享資源競爭的鎖。通過鎖消除,可以節省毫無意義的請求鎖時間。
下面這種這種情況,我們使用vector, 而vector內部使用了synchronize請求鎖。
public String [] createStrings(){
Vector<String> v= new Vector<String>();
for(int i=0;i<100;i++){
v.add(Integer.toString(i));
}
return v.toArray(new String[]{});
}
由於V只在函式 createStrnigs 中使用,因此它只是一個單純的區域性變數。區域性變數是線上程棧上分配的,屬於執行緒私有額資料,因此不可能被其他執行緒訪問。所以,在這種情況下,Vector內部所有加鎖同步都是沒有必要的。如果虛擬機器檢測到這種情況,就會將這些無用的鎖操作去除。點選這裡瞭解幾種常見的鎖。
鎖消除涉及的一項關鍵技術為逃逸分析。所謂逃逸分析就是觀察某一個變數是否會逃出某一個作用域。在本例中,變數v顯然沒有逃出createString 函式之外。以此為基礎,虛擬機器才可以大膽的將v內部的加鎖操作去除。如果createStrings 返回的不是String陣列,而是v本身,那麼就認為變數v逃逸出了當前函式,也就是說v有可能被其他執行緒訪問。如是這樣,虛擬機器就不能消除v中的鎖操作。
逃逸分析必須在 -server 模式下進行,可以使用 -XX:DoEscapeAnalysis 引數開啟逃逸分析,使用 -XX:+EliminateLocks 引數可以開啟鎖消除。