Synchronized鎖及其膨脹

一、序言

在並發編程中,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鎖更多的意義是理解鎖的過程。

Tags: