1. 程式人生 > >併發系列(十二)-----ReentrantReadWriteLock的使用

併發系列(十二)-----ReentrantReadWriteLock的使用

一 簡介

ReentrantReadWriteLock翻譯過是讀寫鎖的意思。併發程式設計中我們可能會遇到讀多寫少的情況,面對這種情況的時候我們可以使用ReentrantLock或者synchronized來保證資料的正確性。如果上述兩種方法的話,這樣的話不管是讀操作還是寫操作都要去獲取鎖和是釋放鎖,但是實際上我們可能只需要在寫的時候保證執行緒執行緒是安全的,並且寫完之後讀執行緒得到的是修改後的資料,也就是說,在寫的時候是獨佔的,不允許其他執行緒寫也不允許其他執行緒讀,而當執行緒讀的時候,因為不會做修改的操作這時資料是可以共享的執行緒可以並行的訪問。ReentrantReadWriteLock就可以很好的解決這種讀多寫少的問題。下面是兩種的實現方式的差異

二 使用

ReentrantReadWriteLock內部維護了兩把鎖,其中writeLock是獨佔鎖也而readLock是共享鎖。當使用構造方法初始化的ReentrantReadWriteLock時候內部就會初始化這兩把鎖,其中在使用構造方法的時候可以傳一個布林值,為true返回的兩把公平鎖,false為非公平鎖,當不傳的時候預設為fasle。下面是簡單的使用程式碼

 /**
     * 初始化讀寫鎖
     */
    private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    /**
     * 呼叫readLock()獲取readLock
     */
    private static final Lock readLock = readWriteLock.readLock();
    /**
     * 呼叫writeLock()獲取writeLock
     */
    private static final Lock writeLock = readWriteLock.writeLock();

    public static void main(String[] args) {
        //讀的執行緒
        Runnable readThread = () -> {
            readData();
        };
        //寫的執行緒
        Runnable writeThread = () -> {
            writeData();
        };
        for (int i = 0; i < 10; i++) {
            if (i%2 == 0){
                //偶數獲取寫鎖
                Thread thread = new Thread(writeThread, "執行緒" + i);
                thread.start();
            }
            //其他執行緒都去獲取讀鎖
            Thread thread = new Thread(readThread, "執行緒" + i);
            thread.start();
        }

    }

    public static void writeData() {
        writeLock.lock();//其他執行緒不能讀也不能寫
        try {
            //處理業務邏輯
            System.out.println(Thread.currentThread() + "獲取到寫鎖");
            //寫鎖的時間設定長的的話可以觀察它的獨佔性
            Thread.sleep(10000);
        } catch (Exception e){}finally {
            //沒有正確的釋放鎖可能會導致死鎖
            System.out.println(Thread.currentThread() + "釋放寫鎖");
            writeLock.unlock();
        }
    }

    public static void readData() {
        readLock.lock();//其他執行緒不可寫
        try {
            System.out.println(Thread.currentThread() + "獲取到讀鎖");
            //讀取資料
            Thread.sleep(100);
        }catch (Exception e){}finally {
            System.out.println(Thread.currentThread() + "釋放讀鎖");
            readLock.unlock();
        }
    }

上面使用讀寫鎖的時候是比較清晰的模式都是一種鎖獲取鎖釋放,然後另一個鎖獲取在釋放,對於這種模式的話只要不要忘記去釋放鎖,一般都不會產生死鎖,但在使用中還可能出現下面的模式。


    /**
     * 模式一 先獲取寫在獲取讀
     */
    public static void writeRead() {
        writeLock.lock();
        System.out.println("獲取到寫鎖");
        readLock.lock();
        System.out.println("獲取到讀鎖");
        readLock.unlock();
        System.out.println("釋放讀鎖");
        writeLock.unlock();
        System.out.println("釋放寫鎖");
    }
     /**
     * 模式二 先獲取讀在獲取寫
     */
    public static void readWrite() {
        readLock.lock();
        System.out.println("獲取到讀鎖");
        writeLock.lock();
        System.out.println("獲取到寫鎖");
        writeLock.unlock();
        System.out.println("釋放寫鎖");
        readLock.unlock();
        System.out.println("釋放讀鎖");

    }

上面兩種模式中模式一是可以的,又叫鎖降級,但模式二是不可以模式二會找成死鎖。當然讀寫鎖還一些API可以用來處理中斷和超時和Reentrant相差不大。

三 總結

  1. 適合讀多寫少的場景。
  2. 利用共享模式實現讀鎖,獨佔模式實現寫鎖;
  3. 支援公平和非公平
  4. 寫鎖阻塞寫鎖和讀鎖,讀鎖阻塞寫鎖;