Java 讀寫鎖

對于一個讀寫鎖來說,同一時刻,可以有多個線程拿到讀鎖,只有一個線程拿到寫鎖。一旦一個線程拿到寫鎖,他們?nèi)魏蜗胍@取讀鎖或者寫鎖的線程,都必須等待。

考慮下面這種情況

Thread1: A.readlock -> ... (已經(jīng)拿到讀鎖)
Thread2: A.readlock->... (Thread1拿到讀鎖之后,Thread2也去請求讀鎖)

很簡單,這種情況Thread2也可以順利拿到讀鎖,沒有任何問題。

如果這時候有個Thread3,他在Thread1拿到讀鎖之后,Thread2請求讀鎖之前,去請求寫鎖。

Thread1: A.readlock -> ... (已經(jīng)拿到讀鎖)
Thread3: A.writelock->...(請求寫鎖)
Thread2: A.readlock->... 

那么這種情況下,Thread2和Thread3會繼續(xù)往下執(zhí)行么?
Thread3顯然是要等待的。Thread2呢?答案是:不一定。

這要取決于讀寫鎖的實(shí)現(xiàn)方法。

linux內(nèi)核的rwlock是讀寫鎖的最簡單的參考實(shí)現(xiàn)。它用一個整數(shù)counter代表一個rwlock。0代表沒有人占有鎖,-1代表有一個線程持有著寫鎖, 正整數(shù)n代表有n個線程持有讀鎖。要拿讀鎖時,如果counter小于0, 則繼續(xù)循環(huán)測試,直到counter非負(fù)。然后給counter加1,拿鎖成功。(當(dāng)然,得保證“在counter非負(fù)的情況下加1”這個操作的原子性,一般通過spinlock或者bit spinlock實(shí)現(xiàn))??梢姡绻呀?jīng)有一個線程拿著讀鎖還未釋放,另一個線程獲取讀鎖會立即成功。

這個實(shí)現(xiàn)很簡單,但是存在公平性的問題:寫者可能會被餓死。 如果有很多線程相續(xù)拿到讀鎖然后釋放讀鎖,保持counter的值始終大于0,那寫者就一直拿不到寫鎖。http://lwn.net/Articles/364583/

一個辦法是在rwlock元數(shù)據(jù)中增加一個標(biāo)記,代表是否有寫者在等待讀者。讀者要拿讀鎖時,先要等待這個標(biāo)記的清除。筆者曾經(jīng)在嵌入式環(huán)境中,使用和修改過這樣的讀寫鎖。更加先進(jìn)的方法,是讓等待者排一個FIFO隊(duì)列,比較著名的是MCS lock和CLH lock。

Java的ReentrantReadWriteLock,就是基于CLH算法。
正是由于這個排隊(duì)算法,由于Thread2在Thread3之前,因此Thread2必須等Thread3拿到鎖,做完事情,并且釋放,才能獲得讀鎖。

下面是一個簡單的實(shí)驗(yàn)的代碼

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Main {

    public static void main(String[] args) {
        final ReentrantReadWriteLock.ReadLock readLock;
        final ReentrantReadWriteLock.WriteLock writeLock;
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
        readLock = lock.readLock();
        writeLock = lock.writeLock();

        System.out.println("main: before readLock.lock()");
        readLock.lock();
        System.out.println("main: after readLock.lock()");

        Thread tw = new Thread() {
            @Override
            public void run() {
                System.out.println("tw: before writeLock.lock()");
                writeLock.lock();
                System.out.println("tw: after writeLock.lock()");
            }
        };

        Thread tr = new Thread() {
            @Override
            public void run() {
                System.out.println("tr: before readLock.lock()");
                readLock.lock();
                System.out.println("tr: after readLock.lock()");
            }
        };

        try {
            tw.start();
            Thread.sleep(1000);
            tr.start();
            tw.join();
            tr.join();
        } catch (InterruptedException ie) {
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容