Java同步方法:synchronized到底鎖住了誰?

Java同步方法:synchronized到底鎖住了誰?

目錄
前言
同步方法
類的成員方法
類的靜態方法
同步代碼塊
總結
其他同步方法
參考資料
前言
相信不少同學在上完Java課後,對於線程同步部分的實戰,都會感到不知其然。

比如上課做實驗的時候,按着老師的實驗指導書中的描述完成了多線程的同步操作,就感覺自己已經掌握這個知識點了,實際運用中再次手足無措,就像我一樣。 這裏提問一下:synchronized對方法修飾,在別處調用這個方法時,誰被鎖定了呢?另外,在新建線程中使用synchronized(this){ }結構時,如:

void methodA() {

new Thread(() -> {
    synchronized (this) {
        this.methodB();
    }
}).start();

}
這個被鎖的this又是誰呢?

這篇博文來詳細介紹一下線程同步中涉及synchronized修飾的兩種用法:同步方法和同步代碼塊。

纔不會說這篇是我對一個項目代碼中的線程同步機制感到迷惑而搜資料寫的筆記(

同步方法
先開始介紹synchronized修飾符本身的特性:

synchronized關鍵字不能被繼承 即父類方法是同步方法 子類方法繼承後默認不是同步方法
synchronized不能修飾接口方法 因爲接口是特殊的抽象類 不能新建實例 實例鎖應歸實現其的類所有
synchronized不能修飾構造方法(但可在內部使用synchronized代碼塊來同步
類的成員方法
修飾一個普通方法時,作用域是當前調用對象,即只要還沒出方法的作用域,其他試圖獲取該對象的鎖線程都將被阻塞。

這裏容易誤解的就是,只是嘗試獲取該對象鎖的線程會被阻塞,並不影響其他線程不獲取鎖瞎操作,所以要在涉及同步量操作的所有地方採用同步方法(如加鎖),否則引起線程安全問題幾乎是必然的。

類的靜態方法
因爲類的靜態方法屬於類,而不屬於類的某個特定實例,所以對類的靜態方法修飾直接作用於類本身,相當於synchronized(ClassA.class),即直接鎖定整個類。這裏有不少別人的筆記寫着,直接作用於類的所有對象,我覺得存在歧義,因爲正常情況下,除非採用工廠模式之類的方法,不然很難獲取到所有對象的引用,並且這種表述也是不符合直覺的。

同步代碼塊
由於同步是一個高開銷操作,上面講的同步方法其實是同步代碼塊的一個語法糖,平時應儘量使用synchronized同步關鍵代碼,而不是對整個方法同步,要儘可能減少同步的內容。

對成員方法修飾 -> synchronized(this)
對靜態方法修飾 -> synchronized(ClassA.class)

總結
自己全部測試了一遍,重新驗證了猜想,目測沒有什麼不符合直覺的地方,另外,對單獨信號量,如byte[]之類的加鎖操作,如果不釋放鎖,其他線程會全部阻塞在獲取鎖的過程中,這裏不單列出來。

本文前言中提到的問題,答案即爲新建這個線程的實例本身,而不是這個被新建的線程類。

這裏看到結果就容易理解了,每個對象都自己與一個鎖相關聯,類靜態本身也與一個鎖關聯,任何嘗試獲取鎖的方法纔可能會引起阻塞。

修飾對象/其他線程 同實例

其他實例

阻塞/不阻塞 成員變量 非同步方法 同步方法 成員變量 非同步方法 同步方法 靜態變量 靜態非同步方法 靜態同步方法
this 不阻塞 不阻塞 阻塞 不阻塞

不阻塞
不阻塞
類的成員方法 不阻塞 不阻塞 阻塞
不阻塞

類.class -

阻塞

類的靜態方法
阻塞

其他同步方法
這裏就不多介紹了,下面遇到了再詳細寫。

使用volatile修飾域 每次使用此域都需重新計算
使用ReentrantLock可重入鎖 需要注意及時手動釋放 通常在finally裏釋放
使用ThreadLocal 這裏反對本文參考資料中的一個介紹 嚴格來說這不叫同步 只是各個使用到相同類的線程 獨立的創建一份自己的副本 由於這個副本僅當前線程可達 也就沒有了其他線程的競爭 相當於線程內部的全局變量 應用場景主要有兩種 一是單個線程中多個類的實例共享另一個實例的時候 如數據庫連接、RequestContextHolder、Web Session、日誌的MDC、SimpleDateFormat(線程不安全的工具類)等 二是避免超長參數傳遞鏈 避免在方法中來回傳遞參數
使用LinkedBlockingQueue阻塞隊列 利用隊列FIFO(先進先出)的特性實現生產者-消費者模型
使用Atomic原子變量 利用原子操作本身的特性實現多線程同步
參考資料
java-synchronized-method-lock-on-object-or-method - stackoverflow
what-is-the-reason-why-synchronized-is-not-allowed-in-java-8-interface-methods - stackoverflow
Java線程同步的7種方式 - cnblogs
關於Java的構造方法在類初始化和類實例化中的實質 - CSDN
Java中Synchronized的用法 - CSDN
Java多線程安全之構造函數 - CSDN
正確理解Thread Local的原理與適用場景 - 個人博客
理解Java中的ThreadLocal - 個人博客
Java中的四種引用類型(強、軟、弱、虛) - 簡書

本文作者:Licsber
出處:https://www.cnblogs.com/licsber/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章