線程基礎(三十四)-Read-Write Lock Pattern

本文作者:王一飛,叩丁狼高級講師。原創文章,轉載請註明出處。

接上篇,本篇講解線程另外一個設計模式:Read-Write Lock Pattern.

概念

Read是讀, 指獲取/查詢數據的線程, Write是寫,指操作(增刪改)數據的線程.

Read-Write Lock 模式要求:
1>在讀模式時,多個線程可以同時執行讀操作,但不允許寫操作
2>在寫模式時,只允許一個線程執行寫操作,不允許其他線程進行讀寫
簡單講就是常說的:讀寫互斥

參與角色

Reader
讀線程,擁有對共享資源讀操作權限
Writer
寫線程,擁有對共享資源寫操作權限
Resource
共享資源, Reader/writer角色共享的資源, 一般具有2個方法,一個不改變資源狀態的讀操作, 一個可改變資源狀態的寫操作。
讀寫鎖
鎖對象,提供讀鎖, 寫鎖,在reader/writer角色讀寫時,加鎖實現讀寫互斥

演示案例

需求:5個線程讀,5個線程寫實現讀寫互斥
Read-Write Lock Pattern難點在於怎麼控制讀寫互斥,而讀寫互斥可以拆分爲:
1>讀取與寫入的衝突
2>寫入與寫入的衝突
3>寫入與讀取的衝突

思考:讀寫怎麼互斥法
1:讀模式時(線程嘗試讀操作)
a>如果此時已經有線程在讀,允許讀
b>如果此時已經有線程在寫,暫停當前線程

2:寫模式時(線程嘗試寫操作)
a>如果此時已經有線程在讀,暫定當前線程
b>如果此時已經有線程在寫,暫停當前線程

根據上面分析,很容易可以找到需求突破口
1>線程滿足某個條件需要暫停等待----線程wait/notifyall操作
2>這裏某個條件是當前在讀/在寫的線程數量-----線程計數
3>使用同一鎖對象對多線程的讀寫互斥控制

結論:
設計:ReadWriteLock 讀寫鎖控制類
readCount : 當前讀線程數量
writeCount: 當前寫線程數量
readLock(): 獲取讀鎖,成功執行,不成功等待
unReadLock(): 操作結束後釋放讀鎖
writeLock: 獲取寫鎖,成功執行,不成功等待
unWriteLock: 操作結束後釋放寫鎖

//互斥鎖控制類
public class ReadWriteLock {

    //讀線程個數
    private int readCount;

    //寫線程個數
    private int writeCount;

    public synchronized String info(){
        return "讀線程:" + readCount + ", 寫線程:" + writeCount;
    }

    //獲取讀鎖
    public synchronized void readLock() throws InterruptedException {
        //此時有寫線程操作,暫停
        while(writeCount > 0 ){
            wait();
        }
        //沒有讀寫線程+1
        readCount++;
        System.out.println("讀模式:" +info());
    }

    //釋放讀鎖
    public synchronized void unReadLock(){
        readCount--; //讀線程減少
        notifyAll(); //喚醒等待線程:讀或者寫
    }


    //獲取寫鎖
    public synchronized void writeLock() throws InterruptedException {

        //此時有讀線程操作,暫停
        //此時有寫線程操作,暫停
        while(readCount > 0 || writeCount > 0){
            wait();
        }

        //沒有讀寫線程+1
        writeCount++;
        System.out.println("寫模式:" +info());
    }

    //釋放寫鎖
    public synchronized void unWriteLock(){
        writeCount--; //讀線程減少
        notifyAll(); //喚醒等待線程:讀或者寫
    }
}

操作的資源
//共享資源

//共享資源
public class Resource {

    //讀與寫數據
    private String data = "init";

    //控制data數據的讀寫互斥
    private ReadWriteLock lock = new ReadWriteLock();

    //對data數據的讀操作
    public void read() throws InterruptedException {

        lock.readLock();
        try {
            System.out.println(Thread.currentThread().getName()+"--read--:" + data);
        }finally {
            lock.unReadLock();
        }
    }


    //對data數據的寫操作
    public void write(String d) throws InterruptedException {

        lock.writeLock();
        try {
            data = d;
            System.out.println(Thread.currentThread().getName()+" -write-:" + data);
        }finally {
            lock.unWriteLock();
        }
    }

}

測試類

public class App {
    public static void main(String[] args) {

        final Resource data = new Resource();

        //5個讀
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                public void run() {
                    while (true){
                        try {
                            data.read();
                            Thread.sleep(new Random().nextInt(200));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },"read_" + i).start();

        }

        //5個寫
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                public void run() {
                    while (true){
                        try {
                            data.write(Thread.currentThread().getName());
                            Thread.sleep(new Random().nextInt(200));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },"write_" + i).start();

        }

    }
}

執行效果:

讀模式:讀線程:1, 寫線程:0
read_0--read--:init
讀模式:讀線程:2, 寫線程:0
read_3--read--:init
讀模式:讀線程:3, 寫線程:0
read_2--read--:init
讀模式:讀線程:4, 寫線程:0
read_1--read--:init
讀模式:讀線程:1, 寫線程:0
read_4--read--:init
寫模式:讀線程:0, 寫線程:1
write_0 -write-:write_0
寫模式:讀線程:0, 寫線程:1
write_1 -write-:write_1
寫模式:讀線程:0, 寫線程:1
write_2 -write-:write_2
寫模式:讀線程:0, 寫線程:1
write_3 -write-:write_3
寫模式:讀線程:0, 寫線程:1
write_4 -write-:write_4
寫模式:讀線程:0, 寫線程:1
write_0 -write-:write_0
讀模式:讀線程:1, 寫線程:0
read_3--read--:write_0
寫模式:讀線程:0, 寫線程:1
write_4 -write-:write_4
讀模式:讀線程:1, 寫線程:0
read_1--read--:write_4
讀模式:讀線程:1, 寫線程:0
read_4--read--:write_4
讀模式:讀線程:1, 寫線程:0
read_2--read--:write_4
寫模式:讀線程:0, 寫線程:1
write_0 -write-:write_0
寫模式:讀線程:0, 寫線程:1
write_1 -write-:write_1
讀模式:讀線程:1, 寫線程:0

從輸出效果看, 當讀線程有值時,不會進行寫操作, 但讀操作可以同時進行,比如:讀模式時,讀線程:4,寫線程爲:0。 反之,當寫線程出現1時,寫線程全部爲0, 另外寫線程個數不會超過1

使用場景

Read-Write Lock Pattern講究是讀寫互斥,讀不進行安全控制, 寫時需要控制。相對讀寫都進行安全控制的模式來說,性能上還是有一定提升, 但不絕對。Read-Write Lock Pattern更使用與讀頻率遠高與寫頻率的安全操作。

jdk中其實也有讀寫鎖操作

//共享資源
public class Resource {

    //讀與寫數據
    private String data = "init";

    //控制data數據的讀寫互斥
   // private ReadWriteLock lock = new ReadWriteLock();

    //jdk
    private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    //對data數據的讀操作
    public void read() throws InterruptedException {

        //lock.readLock();
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"--read--:" + data);
        }finally {
            //lock.unReadLock();
            readLock.unlock();
        }
    }


    //對data數據的寫操作
    public void write(String d) throws InterruptedException {

        //lock.writeLock();
        writeLock.lock();
        try {
            data = d;
            System.out.println(Thread.currentThread().getName()+" -write-:" + data);
        }finally {
           // lock.unWriteLock();
            writeLock.unlock();
        }
    }

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