天天看點

讀寫鎖 ReadWriteLock 适用場景

前言

在沒有讀寫鎖之前,ReentrantLock 雖然可以保證了線程安全,但是也浪費了一定的資源,因為如果多個讀操作同時進行,其實并沒有線程安全問題,我們可以允許讓多個讀操作并行,以便提高程式效率。

但是寫操作不是線程安全的,如果多個線程同時寫,或者在寫的同時進行讀操作,便會造成線程安全問題。

我們的讀寫鎖就解決了這樣的問題,它設定了一套規則,既可以保證多個線程同時讀的效率,同時又可以保證有寫入操作時的線程安全。

整體思路是它有兩把鎖,第 1 把鎖是寫鎖,獲得寫鎖之後,既可以讀資料又可以修改資料,而第 2 把鎖是讀鎖,獲得讀鎖之後,隻能檢視資料,不能修改資料。讀鎖可以被多個線程同時持有,是以多個線程可以同時檢視資料。

在讀的地方合理使用讀鎖,在寫的地方合理使用寫鎖,靈活控制,可以提高程式的執行效率。

1.讀寫鎖的擷取規則

我們在使用讀寫鎖時遵守下面的擷取規則:

如果有一個線程已經占用了讀鎖,則此時其他線程如果要申請讀鎖,可以申請成功。

如果有一個線程已經占用了讀鎖,則此時其他線程如果要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖,因為讀寫不能同時操作。

如果有一個線程已經占用了寫鎖,則此時其他線程如果申請寫鎖或者讀鎖,都必須等待之前的線程釋放寫鎖,同樣也因為讀寫不能同時,并且兩個線程不應該同時寫。

一句話總結:要麼是一個或多個線程同時有讀鎖,要麼是一個線程有寫鎖,但是兩者不會同時出現。也可以總結為:讀讀共享、其他都互斥(寫寫互斥、讀寫互斥、寫讀互斥)。

2.使用案例

下面我們舉個例子來應用讀寫鎖,ReentrantReadWriteLock 是 ReadWriteLock 的實作類,最主要的有兩個方法:readLock() 和 writeLock() 用來擷取讀鎖和寫鎖。

代碼如下:

public class ReadWriteLockDemo {

    private static final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
    private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

    public static void main(String[] args) {
        new Thread(()->read()).start();
        new Thread(()->read()).start();
        new Thread(()->write()).start();
        new Thread(()->write()).start();
    }

    private static void read() {
        String threadName = Thread.currentThread().getName();
        readLock.lock();
        try {
            System.out.println(threadName + "-擷取讀鎖(readLock),讀取資料...");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(threadName + "-釋放讀鎖(readLock)");
            readLock.unlock();
        }
    }

    private static void write() {
        String threadName = Thread.currentThread().getName();
        writeLock.lock();
        try {
            System.out.println(threadName + "-擷取寫鎖(writeLock),寫入資料...");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(threadName + "-釋放讀鎖(writeLock)");
            writeLock.unlock();
        }
    }

}
           

執行結果:

Thread-0-擷取讀鎖(readLock),讀取資料...
Thread-1-擷取讀鎖(readLock),讀取資料...
Thread-0-釋放讀鎖(readLock)
Thread-1-釋放讀鎖(readLock)
Thread-2-擷取寫鎖(writeLock),寫入資料...
Thread-2-釋放寫鎖(writeLock)
Thread-3-擷取寫鎖(writeLock),寫入資料...
Thread-3-釋放寫鎖(writeLock)
           

可以看到

線程0和線程1都獲得了讀鎖,讀取資料;

但是線程2獲得寫鎖之後,線程3線上程2釋放了寫鎖之後,才擷取寫鎖,寫入資料。

3.讀寫鎖适用場合

最後我們來看下讀寫鎖的适用場合,相比于 ReentrantLock 适用于一般場合,ReadWriteLock 适用于讀多寫少的情況,合理使用可以進一步提高并發效率。

繼續閱讀