讀寫鎖 是高併發系統中實現「多讀單寫、讀寫互斥」的核心同步原語。JDK 1.5+ 在 java.util.concurrent.locks 中提供了基於 AQS 的 ReentrantReadWriteLock,但其作者 Doug Lea 早在 JDK 1.4 時代便在 EDU.oswego.cs.dl.util.concurrent 包中實現了多種讀寫鎖變體。本文深度剖析這一經典實現的核心源碼,揭示讀寫鎖設計的本質邏輯。
ReadWriteLock (Interface)
└── WriterPreferenceReadWriteLock (寫優先,基礎實現)
├── ReentrantWriterPreferenceReadWriteLock (可重入寫優先)
└── ReaderPreferenceReadWriteLock (讀優先)
└── FIFOReadWriteLock (先進先出公平鎖)
四種實現滿足不同公平性與重入需求,核心邏輯集中在 WriterPreferenceReadWriteLock。
public interface ReadWriteLock {
/** 獲取讀鎖,實現 Sync 介面(含 acquire/release) */
Sync readLock();
/** 獲取寫鎖 */
Sync writeLock();
}
Sync 介面定義 acquire()(阻塞獲取)與 release()(釋放喚醒),對應後來 JDK 中 Lock 的 lock()/unlock()。
/** 活躍讀線程計數 */
protected long activeReaders_ = 0;
/** 當前持有寫鎖的線程 */
protected Thread activeWriter_ = null;
/** 等待中的讀線程計數(策略調整用) */
protected long waitingReaders_ = 0;
/** 等待中的寫線程計數(寫優先關鍵) */
protected long waitingWriters_ = 0;
/** 內部類實例:讀鎖與寫鎖 */
protected final ReaderLock readerLock_ = new ReaderLock();
protected final WriterLock writerLock_ = new WriterLock();
public void acquire() throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
InterruptedException IE = null;
synchronized (this) {
// 嘗試直接獲取
if (!startReadFromNewReader()) {
// 失敗則進入等待循環
for (;;) {
try {
ReaderLock.this.wait(); // 釋放監視器鎖,等待 notify
// 被喚醒後重試(注意:寫優先策略下,喚醒不代表能獲取)
if (startReadFromWaitingReader())
return;
} catch (InterruptedException ex) {
cancelledWaitingReader();
IE = ex;
break;
}
}
}
}
if (IE != null) {
writerLock_.signalWaiters(); // 中斷時嘗試喚醒寫者
throw IE;
}
}
// 新讀者嘗試獲取
protected synchronized boolean startReadFromNewReader() {
boolean pass = startRead();
if (!pass) ++waitingReaders_; // 失敗計入等待數
return pass;
}
// 等待中的讀者重試
protected synchronized boolean startReadFromWaitingReader() {
boolean pass = startRead();
if (pass) --waitingReaders_;
return pass;
}
// 核心準入條件:寫優先關鍵
protected boolean allowReader() {
// 必須:無活躍寫者 AND 無等待寫者
return activeWriter_ == null && waitingWriters_ == 0;
}
protected synchronized boolean startRead() {
boolean allowRead = allowReader();
if (allowRead) ++activeReaders_;
return allowRead;
}
public void release() {
Signaller s = endRead();
if (s != null) s.signalWaiters(); // 喚醒等待者
}
protected synchronized Signaller endRead() {
// 關鍵:只有最後一個讀者釋放且有等待寫者時,喚醒寫鎖
if (--activeReaders_ == 0 && waitingWriters_ > 0)
return writerLock_; // 返回寫鎖信號器,喚醒一個寫者
return null; // 其他情況不喚醒
}
public void acquire() throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
InterruptedException IE = null;
synchronized (this) {
if (!startWriteFromNewWriter()) {
for (;;) {
try {
WriterLock.this.wait();
if (startWriteFromWaitingWriter())
return;
} catch (InterruptedException ex) {
cancelledWaitingWriter();
WriterLock.this.notify(); // 中斷時喚醒下一個寫者
IE = ex;
break;
}
}
}
}
if (IE != null) {
readerLock_.signalWaiters(); // 中斷時喚醒讀者
throw IE;
}
}
protected synchronized boolean startWriteFromNewWriter() {
boolean pass = startWrite();
if (!pass) ++waitingWriters_;
return pass;
}
protected synchronized boolean startWrite() {
// 寫鎖準入:無活躍寫者 AND 無活躍讀者
boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);
if (allowWrite) activeWriter_ = Thread.currentThread();
return allowWrite;
}
protected synchronized boolean startWriteFromWaitingWriter() {
boolean pass = startWrite();
if (pass) --waitingWriters_;
return pass;
}
public void release() {
Signaller s = endWrite();
if (s != null) s.signalWaiters();
}
protected synchronized Signaller endWrite() {
activeWriter_ = null;
// 釋放策略:讀優先於寫(但受 allowReader 制約,即寫優先)
if (waitingReaders_ > 0 && allowReader())
return readerLock_; // 喚醒所有等待讀者
else if (waitingWriters_ > 0)
return writerLock_; // 喚醒一個等待寫者
return null;
}
interface Signaller {
void signalWaiters();
}
// ReaderLock 實現:notifyAll 喚醒所有等待讀者
// WriterLock 實現:notify 喚醒單一等待寫者
釋放鎖時返回對應的 Signaller(readerLock_ 或 writerLock_),由調用者統一 signalWaiters(),避免在持有鎖時喚醒導致的活性問題。
allowReader() 要求 waitingWriters_ == 0,有寫者等待時新讀者直接阻塞endRead() 最後一個讀者離開且 waitingWriters_ > 0 時返回 writerLock_,喚醒寫者endWrite() 優先檢查 waitingWriters_ > 0,有寫者等待則喚醒寫者而非讀者三重機制確保:寫線程一旦進入等待隊列,後續新讀線程無法插隊,寫線程將依次獲取鎖。
繼承 WriterPreferenceReadWriteLock,覆寫 allowReader() 與 startWrite(),引入 firstWriter_ 記錄首個等待寫線程,確保嚴格 FIFO 順序。源碼較長,核心思想:用隊列維護請求順序,不區分讀寫,誰先請求誰先獲取。
Doug Lea 早期實現雖基於 wait/notify 而非 AQS,但清晰展示了讀寫鎖設計的三大核心決策點:
allowReader() 單一方法控制整個公平性語義Signaller 解耦釋放與喚醒,notifyAll 喚醒讀者、notify 喚醒寫者理解這套經典實現,能幫助你深度掌握 JDK ReentrantReadWriteLock(AQS 版)的狀態機設計、公平/非公平模式差異、鎖降級原理,並在面對「寫饑餓、鎖升級死鎖、Condition 僅支援寫鎖」等進階問題時有本質認知。