synchronized 是java中常見的保證多執行緒訪問共享資源時的安全的一個關鍵字,很多人在講到synchronized 時都說synchronized 是一把重量級的鎖,那么synchronized 真的很重么?
synchronized 在jdk 1.6以前(不包括1.6)的確是一把很重的鎖,每次使用鎖的時候都是直接向作業系統請求的,所以效率低,且占資源,但是在jdk1.6以后,jvm對synchronized 進行了優化,加入了鎖升級的功能,使得synchronized 在一些情況下并不是一把重量級的鎖,而是一個很輕的一把鎖,
本文就來探討一下,jdk對synchronized 優化,包括鎖升級、鎖粗化、鎖消除,
一、鎖升級
鎖升級其實是指,隨著多執行緒并發加鎖的程度提高而相應的對鎖的狀態的升級,可以分為:偏向鎖、輕量級鎖、自旋鎖、重量級鎖;
偏向鎖
偏向鎖不是一把鎖,而是代表了當前synchronized 鎖狀態,偏向鎖是一把很輕的鎖,當只有一個來執行緒加鎖的時候,此時synchronized 鎖就會變成偏向鎖,偏向鎖代表這個鎖偏向這個執行緒,就是說當這個執行緒再次來加鎖的時候,不需要再向作業系統申請資源,而是很快就能獲取到鎖,減少了申請鎖的開銷,
為什么需要偏向鎖,因為在大多數情況下,多執行緒競爭同一把鎖的情況是很少的,那么在沒有多執行緒并發競爭的情況下,其實沒必要再去向系統申請重量級鎖了,我就用目前這把偏向鎖就夠了,因為申請重量級會耗費比較大的資源,
輕量級鎖
當隨著更多執行緒來加鎖的時候,偏向鎖就會無法滿足使用的條件了,因為偏向鎖認為加鎖的執行緒只有一個,
那么多執行緒加鎖有可能會出現這種情況,當會有兩個及以上的執行緒來加鎖,但是沒有出現同時來競爭鎖的情況,也就是說雖然有多個執行緒來加鎖,可能會出現A執行緒加完鎖之后釋放了鎖,此時B來加鎖,發現并沒有執行緒持有鎖,也就說沒有執行緒跟B來競爭,也就相當于多執行緒來交替加鎖的情況,
當出現這種情況的時候,偏向鎖就會升級為輕量級鎖,輕量級鎖就是指雖然可能會出現多執行緒來加鎖的情況,但是并不存在鎖競爭的情況,并不會存在鎖沖突,
自旋鎖
上面說到隨著加鎖的執行緒變多,出現了多執行緒交替加鎖的情況,偏向鎖會升級為輕量級鎖,但是隨著并發加鎖的執行緒越來越多,出現了多個執行緒同時來加鎖的情況,也就不是交替加鎖,那么此時輕量級鎖已經不適合使用了,但是jvm為了防止鎖直接升級為重量級鎖(因為掛起執行緒和恢復執行緒的操作都需要轉入到內核態中完成,這些操作給系統的并發性能帶來很大的壓力),加入了執行緒自旋的機制,所謂的自旋就是雖然加鎖失敗了,但是有可能出現其他執行緒很快就釋放鎖的情況,那么就嘗試不斷的自旋來嘗試加鎖,而不是直接將鎖升級為重量級鎖,
自旋鎖的好處就是用來減少作業系統的壓力,
重量級鎖
但是隨著加鎖的執行緒不斷增多,自旋了一定的時間或者次數也沒有成功加鎖,那么鎖就會升級為重量級鎖,因為自旋會消耗cpu,一直這么自旋也不是很好的選擇,所以就會升級為重量級鎖,升級為重量級鎖之后,所有來加鎖的執行緒加鎖失敗之后,就會加入等待的佇列中,等待別的執行緒釋放鎖之后再進行鎖的競爭,
通過以上的分析,我們也看出了,synchronized 在最開始的時候并不是上來就是一把重量級的鎖,而是隨著多執行緒并發競爭鎖的激烈程度的提高來不斷的升級,慢慢變成重量級鎖,在整個升級的程序會經歷偏向鎖、輕量級鎖、自旋鎖、重量級鎖的程序,
二、鎖消除
先來看一段代
public class SynchronizedDemo {
public static void main(String[] args) {
Object monitor = new Object();
synchronized (monitor){
System.out.println("加鎖成功....");
}
}
}
通過上面代碼我們可以看出,synchronized 的鎖物件其實是方法的一個區域變數,那么對于這種情況,不會出現多執行緒競爭同一把Object 物件鎖的情況,那么在運行的時候就會忽略這個synchronized 加鎖的程序,
說的官方一點就是指虛擬機即時編譯器在運行時,對一些代碼上要求同步,但是被檢測到不可能存在共享資料競爭的鎖進行消除,
三、鎖粗化
public class SynchronizedDemo {
private static final Object MONITOR = new Object();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
synchronized (MONITOR) {
System.out.println("加鎖成功....");
}
}
}
}
通過這段代碼的分析,可以看出,回圈內部每次都對同一個物件進行加鎖和解鎖,對于這種一串零碎的操作都對同一個物件加鎖情況,虛擬機將會把加鎖同步的范圍擴展 (粗化)到整個操作序列的外部,以上述代碼為例,也就是擴展到把for回圈這個操作加鎖,這樣只需要加鎖一次就可以了,
總結
所以,通過本篇文章可以看出,jdk對synchronized 其實進行了一系列的優化來盡可能減少加鎖時對于性能的消耗,包括鎖升級、鎖消除、鎖粗化,希望通過本篇文章可以讓你對synchronized 底層又有一個全新的認識,
如果覺得這篇文章對你有所幫助,還請幫忙點贊、在看、轉發一下,碼字不易,非常感謝!
如果覺得這篇文章對你有所幫助,還請幫忙點贊、在看、轉發給更多的人,碼字不易,非常感謝!
往期熱門文章推薦
-
有關回圈依賴和三級快取的這些問題,你都會么?(面試常問)
- Redis分布式鎖實作Redisson 15問
-
萬字+28張圖帶你探秘小而美的規則引擎框架LiteFlow
-
面渣逆襲:Spring三十五問,四萬字+五十圖詳解!建議收藏!
-
7000字+24張圖帶你徹底弄懂執行緒池
-
【SpringCloud原理】OpenFeign原來是這么基于Ribbon來實作負載均衡的
-
【SpringCloud原理】Ribbon核心組件以及運行原理原始碼剖析
-
【SpringCloud原理】OpenFeign之FeignClient動態代理生成原理
-
一文帶你看懂Java中的Lock鎖底層AQS到底是如何實作的
掃碼或者搜索關注公眾號 三友的java日記 ,及時干貨不錯過,公眾號致力于通過畫圖加上通俗易懂的語言講解技術,讓技術更加容易學習,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/485573.html
標籤:其他
上一篇:Java 并發編程

