在Java中,鎖是實現多線程同步的核心機制。不同的鎖適用于不同的場景,理解其實現原理和使用方法對優化性能和避免并發問題至關重要。
一、隱式鎖:synchronized
關鍵字
實現原理
- 基于對象監視器(Monitor):每個Java對象都有一個內置的監視器鎖(monitor lock),通過
synchronized
關鍵字獲取。 - 鎖升級機制(JVM優化):
- 偏向鎖:無競爭時,標記線程ID,避免CAS操作。
- 輕量級鎖:通過CAS競爭鎖,失敗后升級為重量級鎖。
- 重量級鎖:通過操作系統互斥量(mutex)實現線程阻塞。
使用方法
// 1. 同步代碼塊
synchronized (obj) { // 臨界區代碼
}// 2. 同步實例方法
public synchronized void method() { }// 3. 同步靜態方法
public static synchronized void method() { }
適用場景
- 簡單同步需求:無需復雜鎖功能的場景(如可中斷、超時等)。
- 代碼簡潔性優先:自動釋放鎖,避免忘記解鎖的風險。
二、顯式鎖:ReentrantLock
實現原理
- 基于AQS(AbstractQueuedSynchronizer):
- 通過
state
變量(CAS操作)記錄鎖狀態。 - 使用CLH隊列管理等待線程。
- 通過
- 支持公平性:可選擇公平鎖(按排隊順序獲取)或非公平鎖(插隊競爭)。
使用方法
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {// 臨界區代碼
} finally {lock.unlock(); // 必須手動釋放
}// 高級功能示例:嘗試獲取鎖
if (lock.tryLock(1, TimeUnit.SECONDS)) {try { /* ... */ } finally { lock.unlock(); }
}
適用場景
- 復雜鎖需求:需要可中斷、超時、公平性等特性。
- 細粒度控制:如跨方法加鎖解鎖(
synchronized
只能在代碼塊內)。
三、讀寫鎖:ReentrantReadWriteLock
實現原理
- 分離讀鎖(共享)和寫鎖(獨占):
- 讀鎖允許多線程并發讀,寫鎖獨占。
- AQS的
state
高16位記錄讀鎖,低16位記錄寫鎖。
使用方法
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();// 讀操作
readLock.lock();
try { /* 讀數據 */ } finally { readLock.unlock(); }// 寫操作
writeLock.lock();
try { /* 寫數據 */ } finally { writeLock.unlock(); }
適用場景
- 讀多寫少:如緩存系統、高頻查詢場景。
- 數據一致性要求:寫操作需要互斥,讀操作可并發。
四、樂觀鎖:StampedLock
(Java 8+)
實現原理
- 基于票據(Stamp)的鎖機制:
- 支持三種模式:寫鎖、悲觀讀鎖、樂觀讀。
- 樂觀讀不阻塞寫操作,通過驗證Stamp判斷數據一致性。
使用方法
StampedLock stampedLock = new StampedLock();// 樂觀讀
long stamp = stampedLock.tryOptimisticRead();
// 讀取數據
if (!stampedLock.validate(stamp)) {// 數據被修改,升級為悲觀讀鎖stamp = stampedLock.readLock();try { /* 重新讀取數據 */ } finally { stampedLock.unlockRead(stamp); }
}// 寫鎖
long stamp = stampedLock.writeLock();
try { /* 寫數據 */ } finally { stampedLock.unlockWrite(stamp); }
適用場景
- 讀多寫少且容忍數據不一致:如統計、日志處理。
- 極高性能需求:樂觀讀避免鎖競爭,但需處理驗證邏輯。
五、其他鎖機制
1. Condition
條件變量
- 與
ReentrantLock
配合使用,實現線程間協作(類似wait/notify
)。 - 典型場景:生產者-消費者模型。
2. 分布式鎖
- 如基于Redis的
Redisson
或ZooKeeper實現。 - 適用場景:跨JVM或分布式系統同步。
六、鎖的選擇與性能優化
鎖對比表
鎖類型 | 特性 | 性能 | 適用場景 |
---|---|---|---|
synchronized | 自動釋放,非公平鎖 | 低競爭時高效 | 簡單同步需求 |
ReentrantLock | 可中斷、超時、公平鎖 | 高競爭時高效 | 復雜鎖需求 |
ReadWriteLock | 讀寫分離 | 讀多寫少高效 | 緩存、查詢系統 |
StampedLock | 樂觀讀,支持鎖升級 | 極高并發 | 讀多寫少,容忍數據不一致 |
最佳實踐
- 減少鎖粒度:縮小臨界區范圍。
- 避免嵌套鎖:防止死鎖(如按固定順序獲取鎖)。
- 監控鎖競爭:使用JProfiler或JStack分析鎖狀態。
七、總結
- 簡單場景優先選擇
synchronized
(JVM優化成熟)。 - 復雜需求使用
ReentrantLock
或ReadWriteLock
。 - 極致性能考慮
StampedLock
,但需謹慎處理數據一致性。
合理選擇鎖類型,結合性能測試和監控,是構建高效并發系統的關鍵。