Java 5 引入 java.util.concurrent.locks 包,提供了比 synchronized 更靈活、功能更豐富的鎖機制。本文從簡單鎖實現入手,層層遞進講解**可重入性、公平性、條件變量、中斷響應**等核心特性,並給出生產環境的最佳實踐指南。
public class Counter {
private int count = 0;
public int inc() {
synchronized(this) { return ++count; }
}
}
public class Counter {
private final Lock lock = new ReentrantLock();
private int count = 0;
public int inc() {
lock.lock();
try { return ++count; }
finally { lock.unlock(); }
}
}
lock()/unlock(),**必須在 finally 中釋放**tryLock()、lockInterruptibly()、newCondition()、公平/非公平選擇public class SimpleLock {
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException {
while (isLocked) wait(); // 自旋等待
isLocked = true;
}
public synchronized void unlock() {
isLocked = false;
notify(); // 喚醒一個等待線程
}
}
關鍵點:while(isLocked) 防止**虛假喚醒**(spurious wakeup);wait() 釋放監視器鎖並進入等待集合;notify() 喚醒一個線程重新競爭。
public class ReentrantSimpleLock {
private boolean isLocked = false;
private Thread lockedBy = null;
private int lockedCount = 0;
public synchronized void lock() throws InterruptedException {
Thread t = Thread.currentThread();
while (isLocked && lockedBy != t) wait(); // 允許同線程重入
isLocked = true;
lockedBy = t;
lockedCount++;
}
public synchronized void unlock() {
if (Thread.currentThread() == lockedBy) {
if (--lockedCount == 0) {
isLocked = false;
lockedBy = null;
notify();
}
}
}
}
核心邏輯:記錄**持有線程**與**重入計數**,只有計數歸零才真正釋放鎖。
ReentrantLock 內部持有 Sync extends AbstractQueuedSynchronizer,利用 AQS 的**狀態變量 state** 表示鎖計數,**FIFO 隊列**管理等待線程。
| 特性 | 非公平鎖(默認) | 公平鎖 |
|---|---|---|
| 獲取邏輯 | 直接 CAS 嘗試搶佔,失敗再入隊 | 檢查隊列頭是否為自己,否則入隊等待 |
| 吞吐量 | 高(減少上下文切換) | 低(嚴格排隊) |
| 飢餓風險 | 有(新線程可能連續搶佔) | 無(先來先服務) |
| 適用場景 | 高併發、非關鍵路徑 | 嚴格時序要求、避免飢餓 |
// 創建公平鎖 Lock fairLock = new ReentrantLock(true);
lock():阻塞獲取,不可中斷lockInterruptibly():阻塞獲取,**可被中斷**(拋出 InterruptedException),適合取消長時間等待tryLock():非阻塞嘗試,立即返回 booleantryLock(timeout, unit):限時嘗試,支持中斷newCondition():綁定條件變量,實現精細的 await/signalclass BoundedBuffer<T> {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final T[] items = (T[]) new Object[10];
private int putIdx = 0, takeIdx = 0, count = 0;
public void put(T x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) notFull.await();
items[putIdx] = x;
if (++putIdx == items.length) putIdx = 0;
++count;
notEmpty.signal();
} finally { lock.unlock(); }
}
public T take() throws InterruptedException {
lock.lock();
try {
while (count == 0) notEmpty.await();
T x = items[takeIdx];
if (++takeIdx == items.length) takeIdx = 0;
--count;
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}
優勢:一個鎖可綁定多個條件隊列,精確喚醒生產者/消費者,避免 notifyAll() 的驚群效應。
class CachedData {
private final Map<String, String> map = new HashMap<>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock r = rwLock.readLock();
private final Lock w = rwLock.writeLock();
public String get(String key) {
r.lock();
try { return map.get(key); }
finally { r.unlock(); }
}
public String put(String key, String value) {
w.lock();
try { return map.put(key, value); }
finally { w.unlock(); }
}
}
public static <T> T withLock(Lock lock, Callable<T> action) throws Exception {
lock.lock();
try { return action.call(); }
finally { lock.unlock(); }
}
ReentrantLock 通用、ReentrantReadWriteLock 讀多寫少、StampedLock(Java 8)樂觀讀更高性能tryLock(timeout) 檢測、考慮無鎖數據結構(ConcurrentHashMap、LongAdder)monitor / Prometheus 自定義指標從 synchronized 到 Lock,Java 鎖機制經歷了**隱式→顯式、單一→多條件、非公平→可選公平、獨佔→讀寫分離**的演進。掌握 AQS 狀態機、隊列同步、條件變量原理,能讓你在面對高併發、死鎖、性能瓶頸時**有的放矢**。記住:**最好的鎖是不需要鎖的**(無鎖算法、線程封閉、不可變對象),其次是**用對鎖、用好鎖、及時釋放鎖**。