首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java ReadWriteLock 實現機制深度解析(一):Doug Lea 早期 Concurrent 包核心源碼剖析

     編輯:Java 相關知識

前言

讀寫鎖 是高併發系統中實現「多讀單寫、讀寫互斥」的核心同步原語。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。

1. ReadWriteLock 介面定義

public interface ReadWriteLock {
    /** 獲取讀鎖,實現 Sync 介面(含 acquire/release) */
    Sync readLock();
    
    /** 獲取寫鎖 */
    Sync writeLock();
}

Sync 介面定義 acquire()(阻塞獲取)與 release()(釋放喚醒),對應後來 JDK 中 Lock 的 lock()/unlock()。

2. WriterPreferenceReadWriteLock - 寫優先鎖核心實現

關鍵狀態變數

/** 活躍讀線程計數 */
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();

ReaderLock 實現詳解

獲取讀鎖

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; // 其他情況不喚醒
}

WriterLock 實現詳解

獲取寫鎖

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;
}

Signaller 機制:解耦喚醒邏輯

interface Signaller {
    void signalWaiters();
}

// ReaderLock 實現:notifyAll 喚醒所有等待讀者
// WriterLock 實現:notify 喚醒單一等待寫者

釋放鎖時返回對應的 Signaller(readerLock_ 或 writerLock_),由調用者統一 signalWaiters(),避免在持有鎖時喚醒導致的活性問題。

3. 寫優先機制的三重保障

  1. 讀者準入檢查:allowReader() 要求 waitingWriters_ == 0,有寫者等待時新讀者直接阻塞
  2. 讀者釋放喚醒寫者:endRead() 最後一個讀者離開且 waitingWriters_ > 0 時返回 writerLock_,喚醒寫者
  3. 寫者釋放優先喚醒寫者:endWrite() 優先檢查 waitingWriters_ > 0,有寫者等待則喚醒寫者而非讀者

三重機制確保:寫線程一旦進入等待隊列,後續新讀線程無法插隊,寫線程將依次獲取鎖。

4. FIFOReadWriteLock - 先進先出公平鎖

繼承 WriterPreferenceReadWriteLock,覆寫 allowReader() 與 startWrite(),引入 firstWriter_ 記錄首個等待寫線程,確保嚴格 FIFO 順序。源碼較長,核心思想:用隊列維護請求順序,不區分讀寫,誰先請求誰先獲取。

總結與啟示

Doug Lea 早期實現雖基於 wait/notify 而非 AQS,但清晰展示了讀寫鎖設計的三大核心決策點:

  1. 狀態編碼:用計數器而非布林值表達多重讀、重入寫
  2. 公平性策略:allowReader() 單一方法控制整個公平性語義
  3. 喚醒策略:Signaller 解耦釋放與喚醒,notifyAll 喚醒讀者、notify 喚醒寫者

理解這套經典實現,能幫助你深度掌握 JDK ReentrantReadWriteLock(AQS 版)的狀態機設計、公平/非公平模式差異、鎖降級原理,並在面對「寫饑餓、鎖升級死鎖、Condition 僅支援寫鎖」等進階問題時有本質認知。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved