文章目錄
- 一、線程同步的核心目標
- 二、線程安全的判定條件
- 三、同步方式一:synchronized 關鍵字
- 1. 同步代碼塊
- 2. 同步方法
- 四、鎖的釋放與不釋放場景
- 1. 自動釋放鎖的場景
- 2. 不會釋放鎖的場景
- 五、同步方式二:ReentrantLock(顯式鎖)
- 1. 核心特性
- 2. 使用規范
- 六、synchronized 與 ReentrantLock 的對比
- 總結
以下是對線程同步相關知識點的系統整理,涵蓋同步方式、鎖機制、線程安全條件及釋放規則等核心內容:
一、線程同步的核心目標
解決多線程并發操作共享資源時的線程安全問題(如數據不一致、重復修改等),通過控制線程對共享資源的訪問順序,保證操作的原子性、可見性和有序性。
二、線程安全的判定條件
出現線程安全問題必須同時滿足兩個條件:
- 多線程環境:程序運行在多個線程并發執行的場景中。
- 共享資源:多個線程操作的是同一份資源(如共享變量、對象屬性等)。
解決方案:通過“鎖機制”保證同一時間只有一個線程能操作共享資源,核心是所有線程必須使用同一把鎖。
三、同步方式一:synchronized 關鍵字
synchronized
是 Java 內置的隱式鎖機制,通過“監視器(Monitor)”實現,可修飾代碼塊或方法。
1. 同步代碼塊
-
語法:
synchronized (鎖對象) {// 需要同步的代碼(操作共享資源的邏輯) }
-
核心要素:
- 鎖對象:可以是任意對象(通常使用共享資源本身或
this
),但必須保證所有線程使用同一把鎖。 - 同步范圍:僅包含操作共享資源的代碼,范圍越小,效率越高。
- 鎖對象:可以是任意對象(通常使用共享資源本身或
-
示例:
// 共享資源 private int count = 0; private Object lock = new Object(); // 鎖對象// 多線程執行的方法 public void increment() {synchronized (lock) { // 所有線程使用同一把lock鎖count++; // 操作共享資源} }
2. 同步方法
-
語法:直接在方法聲明處添加
synchronized
關鍵字,隱式指定鎖對象。// 實例方法 public synchronized void method1() { ... }// 靜態方法 public static synchronized void method2() { ... }
-
隱式鎖對象規則:
- 實例方法:鎖對象為
this
(當前對象實例)。 - 靜態方法:鎖對象為
類名.class
(當前類的字節碼對象,全局唯一)。
- 實例方法:鎖對象為
-
特點:
- 無需顯式指定鎖對象,簡化代碼,但同步范圍是整個方法(可能降低效率)。
- 靜態同步方法和實例同步方法使用的是不同鎖(類對象 vs 實例對象),兩者不會互斥。
四、鎖的釋放與不釋放場景
synchronized
鎖的釋放由 JVM 自動管理,以下情況會觸發釋放或不釋放:
1. 自動釋放鎖的場景
- 同步代碼塊或同步方法正常執行完畢(執行到代碼塊末尾)。
- 同步代碼中出現未捕獲的異常或錯誤,導致線程終止。
- 同步代碼中調用
wait()
方法(線程進入等待狀態,主動釋放鎖)。 - 同步代碼中通過
break
、return
等語句退出代碼塊/方法。
2. 不會釋放鎖的場景
- 調用
Thread.sleep(long)
:線程休眠時仍持有鎖(其他線程無法獲取)。 - 調用
Thread.yield()
:線程主動讓出 CPU,但鎖未釋放。 - 線程被
suspend()
暫停(已過時方法):暫停期間仍持有鎖,可能導致死鎖。
五、同步方式二:ReentrantLock(顯式鎖)
java.util.concurrent.locks.ReentrantLock
是 JDK 提供的顯式鎖實現,比 synchronized
更靈活。
1. 核心特性
- 可重入性:同一線程可多次獲取同一把鎖(與
synchronized
一致)。 - 公平性:通過構造函數指定是否為公平鎖:
ReentrantLock fairLock = new ReentrantLock(true); // 公平鎖(按請求順序獲取鎖) ReentrantLock nonfairLock = new ReentrantLock(false); // 非公平鎖(默認,可能插隊)
- 公平鎖:線程按請求鎖的順序獲取鎖,避免饑餓,但效率較低。
- 非公平鎖:允許線程“插隊”獲取鎖,效率更高,但可能導致部分線程長期等待。
2. 使用規范
必須手動獲取鎖和釋放鎖,且釋放操作需放在 finally
中,確保鎖一定被釋放:
ReentrantLock lock = new ReentrantLock();public void operation() {lock.lock(); // 手動獲取鎖try {// 操作共享資源(可能出現線程安全的代碼)} finally {lock.unlock(); // 手動釋放鎖,必須在finally中}
}
六、synchronized 與 ReentrantLock 的對比
特性 | synchronized | ReentrantLock |
---|---|---|
鎖類型 | 隱式鎖(JVM 自動管理) | 顯式鎖(手動獲取和釋放) |
公平性 | 僅支持非公平鎖 | 可通過構造函數指定公平/非公平 |
可中斷性 | 不可中斷 | 支持中斷(lockInterruptibly() ) |
條件變量 | 僅支持 wait() /notify() | 支持多個 Condition 對象,更靈活 |
性能 | JDK 1.6 后優化,與 ReentrantLock 接近 | 高并發下略優,功能更豐富 |
總結
- 線程同步的核心是通過鎖機制控制共享資源的并發訪問,需保證多線程使用同一把鎖。
synchronized
適合簡單場景,隱式管理鎖的獲取和釋放,易用性高。ReentrantLock
適合復雜場景(如公平性控制、中斷處理),需手動管理鎖,靈活性更高。- 無論使用哪種鎖,都需注意鎖的釋放時機,避免死鎖(如
sleep()
不釋放鎖可能導致的問題)。
合理選擇同步方式是保證多線程程序正確性和效率的關鍵。