目錄
- 1 鎖優化歷史
- 2 自旋鎖與自適應自旋
- 2.1 關于自旋鎖
- 2.1 自旋鎖優化:自適應自旋
- 3 鎖消除
- 4 鎖粗化
- 5 輕量級鎖
- 6 偏向鎖
1 鎖優化歷史
- synchronized 從 JDK1.0到JDK1.5 ,效率低
- JDK1.5到JDK1.6,JVM團隊對synchronized進行深度優化,加入了:適應性自旋、鎖消除、鎖膨脹、輕量級鎖、偏向鎖 等優化技術
- JDK1.5 開始,加入java.util.concurrent,提供API層面的輕量級鎖應用
為什么優化synchronized?
互斥同步對性能最大的影響是阻塞的實作,掛起執行緒和恢復執行緒的操作都需要轉入內核態中完成,這些操作給Java虛擬機的并發性能帶來了很大的壓力,
2 自旋鎖與自適應自旋
2.1 關于自旋鎖
自旋鎖歷史行程:首次出現在JDK1.4.2,但默認關閉(使用-XX:+UseSpinning引數開啟),在JDK6開始默認開啟,
自旋鎖實作邏輯:如果鎖被其它執行緒占有,那么本執行緒不放棄處理器的執行時間,并執行一個忙回圈(自旋),直至得到鎖,
自旋鎖弊端:如果鎖被占用的時間很長,那么自旋的執行緒只會白白消耗處理器資源,帶來性能的浪費
自旋鎖優化:
- 引入自旋次數:如果自旋超過了限定的次數仍然沒有成功獲得鎖,就應當使用傳統的方式去掛起執行緒,自旋次數的默認值是10次,可以啟用引數-XX:PreBlockSpin來更改,(次數在JDK1.4.2已經實作)
- 引入了自適應自旋
2.1 自旋鎖優化:自適應自旋
原來:所有執行緒的自旋時間統一的(PreBlockSpin配置值 * 單次自旋時間)
自適應自旋:自旋的時間不固定,JVM根據前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定,
自適應自旋情形:
例如1:執行緒A、B,鎖L,A先通過自旋獲取鎖,那么JVM認為B也能通過自旋獲取到鎖,隨即給B分配自旋時間,并且這個時間也是綜合之前的自旋時間
例如2:鎖L,執行緒A B C 通過自旋,都沒有獲取到鎖,那么JVM會對后續的執行緒省略自旋程序,以免浪費處理器資源
3 鎖消除
定義:指虛擬機即時編譯器在運行時,對一些代碼要求同步,但是對被檢測到不可能存在共享資料競爭的鎖進行消除,
判斷依據:源于逃逸分析的資料支持,如果判斷到一段代碼中,在堆上的所有資料都不會逃逸出去被其他執行緒訪問到,那就可以把它們當作堆疊上資料對待,認為它們是執行緒私有的,同步加鎖自然就無須再進行,
舉例:
一段看起來沒有同步的代碼:
public String concatString(String s1, String s2, String s3)
{
return s1 + s2 + s3;
}
javac編譯后,會變為以下同等代碼:
public String concatString(String s1, String s2, String s3) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
sb.append(s3);
return sb.toString();
}
//append()方法有synchronize修飾
分析:
jvm觀察變數sb,經過逃逸分析后會發現它的動態作用域被限制在concatString()方法內部,可以采用無鎖操作,
在解釋執行時這里仍然會加鎖,但在即時編譯之后,這段代碼就會忽略所有的同步措施而直接執行,
鎖消除總結:
JVM會進行逃逸分析,符合條件的,在編譯為機器碼時,會消除所有同步措施
4 鎖粗化
定義:假如一串零碎的操作都對同一個物件加鎖,JVM會把加鎖同步的范圍擴展(粗化)到整個操作序列的外部
舉例:
例如上面concatString()方法,將鎖擴展到第一個append()操作之前最后一個append()操作之后,這樣只需加鎖一次
5 輕量級鎖
邏輯:無競爭的情況下使用CAS操作去消除同步使用的互斥量
輕量級鎖提升性能的依據:“對于絕大部分的鎖,在整個同步周期內都是不存在競爭的”這一經驗法則
程序分析:后續補充
6 偏向鎖
邏輯:在無競爭的情況下把整個同步都消除掉,連CAS操作都不去做
“偏”的理解:鎖會偏向于第一個獲得它的執行緒,如果在接下來的執行程序中,該鎖一直沒有被其他的執行緒獲取,則持有偏向鎖的執行緒將永遠不需要再進行同步,
程序分析:后續補充
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/540129.html
標籤:其他
