2.4.線程的同步和協作_讀寫鎖

讀寫鎖


鎖機制引入了讀寫鎖特性:ReadWriteLock接口和唯一的實現類ReentrantReadWriteLock。讀寫鎖是鎖機制的最大改進之一,提供了將讀和寫分開處理的能力。ReentrantReadWriteLock有兩個鎖,一個是讀操作鎖,另一個是寫操作鎖。讀操作鎖允許多個線程同時訪問,但是寫操作鎖只允許一個線程進行訪問,在一個線程執行寫操作時,其他線程不能執行讀操作。在進行寫操作加鎖時,必須等待所有的讀操作鎖都釋放之後才能實施寫操作加鎖。


我們依然以計數器的例子來說明ReadWriteLock的用法,假設只有一個線程更新計數器的值,通過ReadWriteLock的writeLock方法取得寫操作鎖,進行寫操作的同步;其他線程都是讀取計數器的值用來顯示,通過ReadWriteLock的readLock方法取得讀操作鎖,進行讀操作的同步。示例代碼如下:


public class ReadWriteCountDemo {

    public static void main(String[] args){
        ReadWriteCounter counter = new ReadWriteCounter();
        CountReader reader = new CountReader(counter);
        CountWriter writer = new CountWriter(counter);

        System.out.println("main:創建讀寫線程");
        Thread r1 = new Thread(reader);
        Thread r2 = new Thread(reader);
        Thread r3 = new Thread(reader);
        Thread w = new Thread(writer);

        System.out.println("main:啓動讀寫線程");
        w.start();
        r1.start();
        r2.start();
        r3.start();

        System.out.println("main:等待讀寫線程");
        try {
            w.join();
            r1.join();
            r2.join();
            r3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main:退出");
    }
}

class CountReader implements Runnable{

    ReadWriteCounter counter = null;

    CountReader(ReadWriteCounter counter){
        this.counter = counter;
    }

    @Override
    public void run() {
        for(int i=0 ;i<5; i++){

            long value = counter.get();
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

class CountWriter implements  Runnable{

    ReadWriteCounter counter = null;

    CountWriter(ReadWriteCounter counter){
        this.counter = counter;
    }
    @Override
    public void run() {
        for(int i=0; i<5; i++){

            counter.increase();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }
}

class ReadWriteCounter{
    private long count = 0;
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    public void increase(){
        System.out.println("寫操作:申請寫操作鎖");
        lock.writeLock().lock();
        System.out.println("寫操作:獲得寫操作鎖");
        count++;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("寫操作:完成更新計數器。計數器值:" + count + ",釋放寫操作鎖");
            lock.writeLock().unlock();
        }

    }

    public long get(){
        System.out.println(Thread.currentThread().getName() + ":讀操作:申請讀操作鎖");
        lock.readLock().lock();
        System.out.println(Thread.currentThread().getName() + ":讀操作:獲得讀操作鎖");
        long tmp = count;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println(Thread.currentThread().getName() + ":讀操作:計數器值" + tmp + ",釋放讀操作鎖");
            lock.readLock().unlock();
        }

        return tmp;
    }
}

程序執行日誌:
main:創建讀寫線程
main:啓動讀寫線程
寫操作:申請寫操作鎖
寫操作:獲得寫操作鎖
main:等待讀寫線程
Thread-1:讀操作:申請讀操作鎖
Thread-2:讀操作:申請讀操作鎖
Thread-0:讀操作:申請讀操作鎖
寫操作:完成更新計數器。計數器值:1,釋放寫操作鎖
Thread-1:讀操作:獲得讀操作鎖
Thread-2:讀操作:獲得讀操作鎖
Thread-0:讀操作:獲得讀操作鎖
寫操作:申請寫操作鎖
Thread-0:讀操作:計數器值1,釋放讀操作鎖
Thread-2:讀操作:計數器值1,釋放讀操作鎖
Thread-1:讀操作:計數器值1,釋放讀操作鎖
寫操作:獲得寫操作鎖
Thread-0:讀操作:申請讀操作鎖
Thread-2:讀操作:申請讀操作鎖
Thread-1:讀操作:申請讀操作鎖
寫操作:完成更新計數器。計數器值:2,釋放寫操作鎖
Thread-0:讀操作:獲得讀操作鎖
Thread-2:讀操作:獲得讀操作鎖
Thread-1:讀操作:獲得讀操作鎖
寫操作:申請寫操作鎖
Thread-2:讀操作:計數器值2,釋放讀操作鎖
Thread-1:讀操作:計數器值2,釋放讀操作鎖
Thread-0:讀操作:計數器值2,釋放讀操作鎖
寫操作:獲得寫操作鎖
Thread-2:讀操作:申請讀操作鎖
Thread-0:讀操作:申請讀操作鎖
Thread-1:讀操作:申請讀操作鎖
寫操作:完成更新計數器。計數器值:3,釋放寫操作鎖
Thread-2:讀操作:獲得讀操作鎖
Thread-0:讀操作:獲得讀操作鎖
Thread-1:讀操作:獲得讀操作鎖
Thread-0:讀操作:計數器值3,釋放讀操作鎖
Thread-2:讀操作:計數器值3,釋放讀操作鎖
寫操作:申請寫操作鎖
Thread-1:讀操作:計數器值3,釋放讀操作鎖
寫操作:獲得寫操作鎖
Thread-2:讀操作:申請讀操作鎖
Thread-1:讀操作:申請讀操作鎖
Thread-0:讀操作:申請讀操作鎖
寫操作:完成更新計數器。計數器值:4,釋放寫操作鎖
Thread-2:讀操作:獲得讀操作鎖
Thread-1:讀操作:獲得讀操作鎖
Thread-0:讀操作:獲得讀操作鎖
Thread-2:讀操作:計數器值4,釋放讀操作鎖
寫操作:申請寫操作鎖
Thread-0:讀操作:計數器值4,釋放讀操作鎖
Thread-1:讀操作:計數器值4,釋放讀操作鎖
寫操作:獲得寫操作鎖
Thread-1:讀操作:申請讀操作鎖
Thread-2:讀操作:申請讀操作鎖
Thread-0:讀操作:申請讀操作鎖
寫操作:完成更新計數器。計數器值:5,釋放寫操作鎖
Thread-1:讀操作:獲得讀操作鎖
Thread-0:讀操作:獲得讀操作鎖
Thread-2:讀操作:獲得讀操作鎖
Thread-0:讀操作:計數器值5,釋放讀操作鎖
Thread-2:讀操作:計數器值5,釋放讀操作鎖
Thread-1:讀操作:計數器值5,釋放讀操作鎖
main:退出

可以看到:

  1. 進行寫操作加鎖時,必須等待所有的讀操作鎖釋放。
  2. 寫操作加鎖後,其他讀操作必須等待寫操作鎖釋放之後,才能再進行讀操作加鎖。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章