一、序言
在併發程式設計中,synchronized
鎖因其使用簡單,線上程間同步被廣泛應用。下面對其原理及鎖升級過程進行探究。
二、如何使用
1、修飾例項方法
當例項方法被synchronized修飾時,通過當前例項呼叫此方法的所有執行緒共用一把鎖,不同物件呼叫此方法執行緒間互不影響。
public class A {
public synchronized void func() {
}
}
當使用synchronized鎖修飾例項方法,鎖新增在當前類的例項上,有多少個例項可新增多少把鎖。
2、修飾程式碼塊
修飾程式碼塊比修飾方法顆粒度更小。當例項方法程式碼塊被synchronized修飾時,通過當前例項呼叫此方法的所有執行緒共用一把鎖,不同物件呼叫此方法執行緒間互不影響。
public class B {
public void func() {
synchronized (this) {
}
}
}
當使用synchronized鎖修飾程式碼塊,鎖新增在當前類的例項上,有多少個例項可新增多少把鎖。
3、修飾靜態方法
當靜態方法被synchronized修飾時,整個JVM所有呼叫此方法的執行緒均受同一個鎖的約束。
public class C {
public static synchronized void func() {
}
}
當使用synchronized鎖修飾靜態方法,鎖新增在當前類的類物件
上,最多新增一把鎖。
非必要不使用synchronized修飾靜態方法
三、鎖的升級
Java 8所使用的synchronized鎖是經過優化後的,存在偏向鎖
、輕量級鎖
、重量級鎖
等狀態。
(一)偏向鎖
執行緒間不存在鎖的競爭行為,至多隻有一個執行緒有獲取鎖的需求,常見場景為單執行緒程式
。
1、識別方法
判斷是不是偏向鎖的標識是檢視呼叫此方法的執行緒是否有且僅有一個。
在多執行緒程式設計裡,被鎖修飾的方法僅被單一執行緒呼叫幾乎不存在,因此偏向鎖比較雞肋:如果能夠明確單一執行緒呼叫目標方法,使用無鎖程式設計更為合適。
2、效能比較
無鎖與偏向鎖的效能差異非常接近,幾乎可以忽略不計。
(二)輕量級鎖
執行緒間存在鎖的偽競爭行為
,即同一時刻絕對不會存在兩個執行緒申請獲取鎖,各執行緒儘管都有使用鎖的需求,但是是交替使用鎖。
1、識別方法
當有兩個及以上執行緒呼叫被鎖修飾的方法時,那麼至少能確定是輕量級鎖。
2、效能比較
輕量級鎖由於同一時刻不存在兩個執行緒互相競爭鎖,因此不存線上程阻塞-喚醒
的上下文切換,因此效能相對重量級鎖要高很多。
(三)重量級鎖
執行緒間存在鎖的實質性競爭
行為,執行緒間都有獲取鎖的需求,但是時間不可交錯,互斥鎖的阻塞等待。
1、識別方法
當能夠肯定至少有兩個及以上執行緒呼叫被鎖修飾的方法時,執行緒呼叫方法是隨機的,那麼大概率是重量級鎖。
2、效能比較
重量級鎖由於涉及到執行緒阻塞-喚醒的上下文切換,造成相比較與無鎖狀態,效率低很多。
四、其它內容
(一)鎖的性質
1、公平性
synchronized鎖是非公平鎖
,沒有FIFO佇列機制保障競爭鎖的執行緒一定有機率獲得鎖。
2、重入性
synchronized鎖是可重入鎖
,可重入意味著巢狀呼叫不會產生死鎖問題。
3、樂(悲)觀鎖
synchronized鎖是一種悲觀鎖,通過加鎖實現執行緒間同步。
(二)理解重量級鎖
在多執行緒環境下,如果使用synchronized鎖,那麼大概率會升級到重量級鎖。偏向鎖和輕量級鎖非刻意為之,很難存在,更大的意義是對比幫助理解重量級鎖的效能。
重量級鎖儘管會對效能產生很大影響,但是依舊是解決執行緒間同步的有效手段。
1、選用鎖的建議
當被鎖修飾的方法或者程式碼塊執行時間較長
時,選用基於執行緒阻塞-喚醒切換上下文的方式進行執行緒同步效率相對較高。
當被鎖修飾的方法或者程式碼塊執行時間較短
時,應選用其它替代鎖,比如自旋鎖等。
(三)理解synchronized鎖
在實際多執行緒場景開發中,synchronized
鎖大概率會升級到重量級鎖,因其單向升級的特點,重量級狀態的synchronized
鎖可能會對實際業務的併發產生不利影響,手動選用其它鎖可能會更合適。
synchronized
鎖僅可用於解決同一程式內不同執行緒間同步,對於分散式專案跨進城執行緒同步依賴於分散式鎖,synchronized
鎖更多的意義是理解鎖的過程。