`ReentrantReadWriteLock` 是 Java 並發包中實現「讀讀共存、讀寫互斥、寫寫互斥」的核心鎖。本文從接口定義、內部結構、狀態位運算、獲取/釋放邏輯、公平與非公平策略、重入機制等維度,深度剖析其基於 AQS 的實現原理。
// 接口定義
public interface ReadWriteLock {
Lock readLock(); // 共享鎖
Lock writeLock(); // 獨占鎖
}
// 實現類
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
private final ReadLock readerLock;
private final WriteLock writerLock;
final Sync sync; // 核心同步器,繼承 AQS
public ReentrantReadWriteLock() { this(false); }
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
public Lock readLock() { return readerLock; }
public Lock writeLock() { return writerLock; }
// 內部類:ReadLock、WriteLock 實現 Lock 接口,委託 sync
// 內部類:Sync 繼承 AQS,實現 tryAcquire/tryRelease/tryAcquireShared/tryReleaseShared
// 內部類:FairSync、NonfairSync 繼承 Sync
}
AQS 只有一個 `int state`(32 位),RWLock 巧妙拆分:
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = 1 << SHARED_SHIFT; // 0x00010000
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 0xFFFF
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 0xFFFF
static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 高 16 位
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } // 低 16 位
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c); // 當前寫狀態
if (c != 0) { // 有鎖被持有(讀或寫)
if (w == 0 || current != getExclusiveOwnerThread())
return false; // 有讀鎖、或已有其他線程持有寫鎖 -> 失敗
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires); // 重入:增加寫計數
return true;
}
// 無鎖被持有
if (writerShouldBlock() || !compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively()) throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free) setExclusiveOwnerThread(null);
setState(nextc);
return free; // 完全釋放時返回 true,喚醒等待線程
}
邏輯較複雜,核心要點:
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// 1. 遞減線程級 HoldCounter
// 2. CAS 遞減 state 高 16 位
// 3. 若釋放後 sharedCount == 0 返回 true 喚醒寫線程
// ... (源碼較長,核心是減少共享計數)
}
| 場景 | 非公平 | 公平 |
|---|---|---|
| 寫鎖獲取 | 直接 CAS 搶佔 | 若隊列有前驅則排隊 |
| 讀鎖獲取 | 直接 CAS 搶佔 | 若隊列有前驅則排隊 |
| 吞吐量 | 高 | 低 |
| 飢餓風險 | 寫線程可能飢餓 | 無飢餓 |