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

讀寫鎖算法的 Java 手工實現與飢餓問題深度思考

     編輯:Java 相關知識

問題背景

多執行緒共享資源:寫寫互斥、讀寫互斥、讀讀共存。Java 5+ 提供 ReentrantReadWriteLock,但若僅用 synchronized 如何實現?

設計思路

  1. 業務類 Business 封裝 read()/write()。
  2. write() 直接 synchronized 保證互斥。
  3. read() 使用計數器 readThreads + 同步區塊協調:
  • 進入讀區塊前 synchronized(this) { readThreads++; }。
  • 執行讀邏輯(無鎖區間)。
  • 離開時 synchronized(this) { readThreads--; notifyAll(); }。
  • write() 中 while (readThreads != 0) wait(); 等待所有讀者離開。

完整實現程式碼


package communication;
import java.util.Random;

public class ReadWriteLockTest {
    public static void main(String[] args) {
        final Business business = new Business();
        for (int i = 1; i <= 2; i++) {
            new Thread(() -> {
                for (int j = 1; j < 1000; j++) {
                    business.read();
                    sleep(900);
                }
            }).start();
            new Thread(() -> {
                Random r = new Random();
                for (int j = 1; j < 1000; j++) {
                    business.write(r.nextInt(100));
                    sleep(1000);
                }
            }).start();
        }
    }
    static void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { e.printStackTrace(); } }
}

class Business {
    private int data = 0;
    private int readThreads = 0;

    public void read() {
        synchronized (this) { readThreads++; }
        System.out.println(Thread.currentThread().getName() + " read begin");
        System.out.println(Thread.currentThread().getName() + " read: " + data);
        System.out.println(Thread.currentThread().getName() + " read finish");
        synchronized (this) { readThreads--; this.notifyAll(); }
    }

    public synchronized void write(int i) {
        while (readThreads != 0) {
            try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
        }
        System.out.println(Thread.currentThread().getName() + " write start");
        data = i;
        System.out.println(Thread.currentThread().getName() + " write: " + i);
        System.out.println(Thread.currentThread().getName() + " write over");
        this.notifyAll();
    }
}

邏輯驗證:六種交互情境

  1. 讀者在 readThreads++ 同步區 → 寫者被 synchronized(write) 阻塞。
  2. 讀者在無鎖區 → 寫者進入 while(readThreads!=0) wait() 等待。
  3. 讀者結束 notifyAll → 寫者被喚醒,若仍有其他讀者則再次 wait。
  4. 無讀者時 notifyAll → 讀者與寫者競爭鎖,寫者若得鎖則讀者阻塞在 readThreads++。
  5. 寫者持有鎖期間 → 新讀者阻塞在 readThreads++ 同步區。
  6. 寫者等待喚醒後得鎖 → 同 (5)。

結論:讀寫互斥、寫寫互斥、讀讀並發 目標達成。

深度思考:寫者飢餓問題

當讀者頻繁、持續進入(readThreads 長期 > 0),寫者將永遠在 wait() 中,導致寫者飢餓。

可能解決方向

  • 公平鎖機制:維護等待隊列,寫者到達後禁止新讀者進入(需額外狀態標記)。
  • 讀者限流:限制同時讀者數或引入讀者配額。
  • 升級為 ReentrantReadWriteLock:內建公平/非公平模式,生產環境首選。

總結

純 synchronized 可實現基礎讀寫鎖,但飢餓、可重入、條件變數等進階特性需大量樣板碼。實務上直接使用 java.util.concurrent.locks 成熟實現,手工實現僅供教學驗證併發原理。

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