💡 前言
在多線程并發編程中,線程安全問題始終是開發者需要重點關注的核心內容之一。Java 提供了多種機制來實現同步控制,其中最常用的兩種方式是:
- 使用
synchronized
關鍵字 - 使用
java.util.concurrent.locks.Lock
接口(如ReentrantLock
)
雖然兩者都能實現線程同步功能,但它們在使用方式、靈活性、可擴展性以及性能優化方面存在顯著差異。
本文將從底層原理、語法結構、使用場景、優缺點、最佳實踐等多個維度對 synchronized
和 Lock
進行全面深入的解析,并通過大量代碼示例幫助你更好地理解它們之間的區別與聯系。
📌 一、synchronized
關鍵字詳解
1. 基本概念
synchronized
是 Java 內置的關鍵字,用于保證多個線程對共享資源訪問時的互斥性和可見性。它可以修飾方法或代碼塊,確保同一時刻只有一個線程可以執行被同步的代碼。
2. 使用方式
(1)修飾實例方法
public synchronized void method() {// 同步整個方法體
}
此時鎖對象是當前類的實例(即 this
)。
(2)修飾靜態方法
public static synchronized void staticMethod() {// 同步靜態方法
}
此時鎖對象是當前類的 Class 對象(即 ClassName.class
)。
(3)修飾代碼塊(推薦)
public void method() {synchronized (this) {// 同步代碼塊}
}
更靈活,可以指定任意對象作為鎖,推薦使用這種方式以減少鎖定范圍。
3. 特性總結
特性 | 描述 |
---|---|
自動釋放鎖 | JVM 在同步塊執行結束后自動釋放鎖 |
不可中斷 | 等待獲取鎖的線程無法被中斷 |
非公平鎖 | 多個線程競爭時,不保證先等待的線程優先獲得鎖 |
可重入性 | 支持同一個線程多次獲取同一把鎖 |
🔑 二、Lock
接口詳解(以 ReentrantLock
為例)
1. 基本概念
Lock
是 Java 5 引入的一個接口,位于 java.util.concurrent.locks
包下。常見的實現類有:
ReentrantLock
:可重入鎖ReadWriteLock
:讀寫分離鎖(實現類為ReentrantReadWriteLock
)
相比 synchronized
,Lock
更加靈活和強大,提供了更多高級功能。
2. 使用方式
Lock lock = new ReentrantLock();
lock.lock(); // 手動加鎖
try {// 臨界區邏輯
} finally {lock.unlock(); // 必須放在 finally 塊中釋放鎖
}
?? 注意:必須手動調用 unlock()
,否則可能導致死鎖!
3. 核心特性
特性 | 描述 |
---|---|
手動管理鎖 | 需要顯式調用 lock() 和 unlock() |
可中斷等待 | 支持線程在等待鎖的過程中響應中斷(lockInterruptibly() ) |
超時獲取鎖 | 支持嘗試獲取鎖并設置超時時間(tryLock(long time, TimeUnit unit) ) |
公平鎖/非公平鎖 | 構造函數可選擇是否啟用公平鎖 |
條件變量支持 | 提供 Condition 接口,實現更細粒度的線程通信 |
🤔 三、synchronized
與 Lock
的核心區別對比表
功能 | synchronized | Lock |
---|---|---|
加鎖方式 | 自動加鎖、解鎖 | 手動加鎖、解鎖 |
鎖類型 | 非公平鎖 | 可選公平/非公平 |
可中斷 | ? 不支持 | ? 支持 |
超時機制 | ? 不支持 | ? 支持 |
嘗試獲取鎖 | ? 不支持 | ? 支持 |
條件變量 | ? 不支持 | ? 支持 |
性能優化 | JDK 1.6+ 已優化 | 更適合高并發場景 |
適用場景 | 簡單同步需求 | 復雜并發控制場景 |
🎯 四、使用場景對比與建議
場景 | 推薦使用 | 說明 |
---|---|---|
簡單方法或代碼塊同步 | synchronized | 實現簡單,無需手動釋放鎖 |
高并發、復雜同步控制 | Lock | 提供更多控制選項,如公平鎖、嘗試鎖等 |
需要線程中斷響應 | Lock | synchronized 不支持中斷等待 |
需要條件變量配合 | Lock | Condition 可替代傳統的 wait/notify |
需要超時獲取鎖 | Lock | tryLock() 方法非常實用 |
🧪 五、實戰案例分析
案例 1:帶超時的鎖獲取(適用于防止死鎖)
Lock lock = new ReentrantLock();boolean isLocked = false;
try {isLocked = lock.tryLock(3, TimeUnit.SECONDS);if (isLocked) {try {// 執行業務邏輯} finally {lock.unlock();}} else {System.out.println("未能在3秒內獲取到鎖");}
} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.println("線程被中斷");
}
案例 2:使用 Condition
實現生產者-消費者模型
class BoundedQueue {private final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();private final Queue<Integer> queue = new LinkedList<>();private final int capacity;public BoundedQueue(int capacity) {this.capacity = capacity;}public void put(int value) throws InterruptedException {lock.lock();try {while (queue.size() == capacity) {notFull.await(); // 等待隊列不滿}queue.add(value);notEmpty.signal(); // 喚醒消費者} finally {lock.unlock();}}public int take() throws InterruptedException {lock.lock();try {while (queue.isEmpty()) {notEmpty.await(); // 等待隊列不空}return queue.poll();} finally {lock.unlock();}}
}
🧠 六、底層原理簡析(進階)
1. synchronized
的底層實現
在 JVM 層面,synchronized
是基于 Monitor(監視器)機制實現的。每個 Java 對象都關聯一個 Monitor,當線程進入同步塊時,會嘗試獲取該對象的 Monitor,成功則進入,失敗則阻塞。
JVM 對其進行了多項優化,包括:
- 偏向鎖(Biased Locking)
- 輕量級鎖(Lightweight Locking)
- 自旋鎖(Spin Lock)
- 鎖粗化(Lock Coarsening)
- 鎖消除(Lock Elimination)
這些優化使得 synchronized
在現代 JVM 上表現優異。
2. ReentrantLock
的底層實現
ReentrantLock
底層依賴于 AbstractQueuedSynchronizer
(AQS)框架,是一個基于 CLH(Craig, Landin, and Hagersten)隊列的同步工具。
它通過 CAS(Compare and Swap)操作和 volatile 變量實現線程安全,具有更高的可控性和靈活性。
🛠? 七、最佳實踐與注意事項
建議 | 說明 |
---|---|
優先考慮 synchronized | 如果只是簡單的同步,優先使用 synchronized ,避免復雜代碼 |
Lock 放在 finally 中釋放 | 防止因異常導致死鎖 |
使用 tryLock() 防止死鎖 | 在某些情況下,嘗試獲取鎖比無限等待更合理 |
避免嵌套鎖 | 容易引發死鎖,應盡量避免或使用工具檢測 |
選擇公平鎖需謹慎 | 公平鎖雖然保證順序,但可能帶來性能損耗 |
使用 Condition 替代 wait/notify | 更清晰、線程安全 |
📘 八、總結
項目 | synchronized | Lock |
---|---|---|
是否內置 | ? 是 | ? 否 |
使用難度 | 簡單 | 復雜 |
控制粒度 | 粗 | 細 |
功能豐富度 | 一般 | 強大 |
性能表現 | 好 | 更好(高并發) |
推薦用途 | 初學者、簡單同步 | 高級用戶、復雜并發控制 |
在實際開發中,兩者各有優勢,選擇哪一個取決于具體的應用場景和團隊技術棧。對于大多數中小型項目,synchronized
已經足夠;而在需要更高并發控制能力的場景下,Lock
更具優勢。
🎯 點贊、收藏、轉發本文,讓更多開發者受益!