JDK 1.5 引入 java.util.concurrent.locks 包,提供了比 synchronized 更強大、更靈活的顯式鎖機制。核心接口 Lock 及其經典實現 ReentrantLock、ReentrantReadWriteLock,解決了內置鎖無法中斷、無超時、無公平性、單一條件隊列等痛點,是高併發系統開發的利器。
public interface Lock {
void lock(); // 獲取鎖,阻塞直到成功
void lockInterruptibly() throws InterruptedException; // 可中斷獲取鎖
boolean tryLock(); // 非阻塞嘗試獲取鎖,立即返回結果
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 超時獲取鎖
void unlock(); // 釋放鎖
Condition newCondition(); // 獲取條件變量 (支持多條件隊列)
}
| 方法 | 阻塞行為 | 可中斷 | 超時控制 | 典型場景 |
|---|---|---|---|---|
| lock() | 無限阻塞 | 否 | 無 | 通用互斥,確信能快速獲取鎖 |
| tryLock() | 不阻塞,立即返回 | 否 | 無 | 非關鍵路徑、無法容忍等待、嘗試性獲取 |
| tryLock(time, unit) | 阻塞指定時間 | 是 | 是 | 有明確 SLA 要求、避免死等、分布式鎖客端 |
| lockInterruptibly() | 無限阻塞 | 是 | 無 | 需響應取消信號、可中斷的長時間任務 |
鐵律: 獲取鎖與釋放鎖必須成對,且 unlock() 必須放在 finally 塊中,防止異常導致鎖永不釋放 (死鎖)。
Lock lock = new ReentrantLock();
// 模式 1: lock()
lock.lock();
try {
// 臨界區操作
} finally {
lock.unlock();
}
// 模式 2: tryLock()
if (lock.tryLock()) {
try {
// 臨界區操作
} finally {
lock.unlock();
}
} else {
// 獲取失敗,執行降級/拋異常/記錄日誌
}
// 模式 3: lockInterruptibly() - 必須拋出或捕獲 InterruptedException
public void doTask() throws InterruptedException {
lock.lockInterruptibly();
try {
// 臨界區操作
} finally {
lock.unlock();
}
}
注意:lockInterruptibly()僅在等待鎖的阻塞狀態下響應中斷;一旦獲得鎖進入運行狀態,interrupt()不會強制釋放鎖,只會設置中斷標誌位。
ReentrantLock 是 Lock 的唯一標準實現,支持可重入 (同一線程可多次獲取鎖,計數器累加)、公平/非公平 策略、條件變量 (Condition)。
// 默認非公平鎖 (吞吐優先,可能導致線程飢餓)
ReentrantLock lock = new ReentrantLock();
// 公平鎖 (按請求順序授予鎖,吞吐較低,但避免飢餓)
ReentrantLock fairLock = new ReentrantLock(true);
高併發下默認非公平鎖性能優勢明顯,除非有嚴格公平性業務需求,否則不建議使用公平鎖。
getHoldCount():當前線程持有鎖的重入次數。isHeldByCurrentThread():當前線程是否持有鎖。getQueueLength():等待獲取鎖的線程估計數。hasQueuedThreads():是否有線程在等待。hasQueuedThread(Thread):指定線程是否在等待。isLocked():鎖是否被任意線程持有。getOwner():返回持有鎖的線程 (僅限監控/診斷)。// 錯誤示例:每次調用創建新鎖對象,無法互斥
public void insert(Thread t) {
Lock lock = new ReentrantLock(); // 錯誤!局部變量
lock.lock();
try { ... } finally { lock.unlock(); }
}
// 正確示例:鎖為實例/類成員,多線程共享同一把鎖
private final Lock lock = new ReentrantLock(); // final 修飾防止引用變更
public void insert(Thread t) {
lock.lock();
try { ... } finally { lock.unlock(); }
}
Condition 將傳統 Object Monitor 的單一等待隊列拆分為多個條件隊列,實現「特定條件下等待、特定條件下喚醒」。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition(); // 未滿條件
final Condition notEmpty = lock.newCondition(); // 未空條件
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(T x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) notFull.await(); // 等待不滿
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal(); // 喚醒消費者
} finally { lock.unlock(); }
}
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) notEmpty.await(); // 等待不空
T x = (T) items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal(); // 喚醒生產者
return x;
} finally { lock.unlock(); }
}
}
優勢: 精準喚醒,避免虛假喚醒導致的無效競爭;支持多條件 (如「未滿」「未空」「優先級高」等) 獨立管理。
針對「讀多寫少」場景,ReadWriteLock 分離讀鎖 (共享鎖)與寫鎖 (排他鎖):
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock(); // 共享鎖
Lock writeLock = rwLock.writeLock(); // 排他鎖
// 讀操作
readLock.lock();
try { return data; } finally { readLock.unlock(); }
// 寫操作
writeLock.lock();
try { data = newValue; } finally { writeLock.unlock(); }
| 特性 | 說明 |
|---|---|
| 可重入性 | 讀鎖/寫鎖均支持可重入;持有寫鎖可降級獲取讀鎖,但讀鎖不能升級為寫鎖 (防止死鎖)。 |
| 公平性 | 構造函數 new ReentrantReadWriteLock(true) 啟用公平模式,按請求順序授予,避免寫線程飢餓。 |
| 鎖降級 | 寫鎖 → 讀鎖 → 釋放寫鎖 → 釋放讀鎖,保證數據可見性,典型用於緩存更新場景。 |
| Condition 支持 | 僅 writeLock 支持 newCondition(),讀鎖不支持條件變量。 |
| 鍵方法 | getReadLockCount(), getReadHoldCount(), isWriteLocked(), getWriteHoldCount() 等監控方法。 |
同步方法 (synchronized) 強制串行;讀寫鎖允許並發讀,吞吐量隨線程數線性增長。
| 維度 | synchronized | Lock (ReentrantLock) |
|---|---|---|
| 易用性 | 關鍵字,隱式獲取/釋放,簡潔 | API 調用,顯式 lock/unlock,易忘釋放 |
| 性能 (JDK 8+) | 偏向鎖/輕量級鎖優化極佳,低競爭下更優 | 高競爭、長持鎖、公平鎖場景下更穩定 |
| 可中斷 | 不支持 | 支持 (lockInterruptibly) |
| 超時獲取 | 不支持 | 支持 (tryLock(time, unit)) |
| 公平性 | 非公平 | 可選 (構造參數) |
| 條件變量 | 單一 wait/notify | 多 Condition 隊列 |
| 讀寫分離 | 不支持 | ReentrantReadWriteLock |
| 死鎖診斷 | 困難 | 提供 getOwner() 等監控 API |
synchronized 代碼最簡潔、JVM 優化最成熟。Atomic*)、並發集合 (ConcurrentHashMap)、LongAdder、ThreadLocal、不可變對象,往往比鎖更高效。掌握 Lock 體系,不僅是 API 的熟悉,更是對並發控制語義 (互斥、共享、條件協作、公平性、可中斷性) 的深度理解。在實際架構中,靈活組合 synchronized、ReentrantLock、ReadWriteLock、原子類與無鎖數據結構,才能構建高性能、高可用的並發系統。