最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中讀寫鎖ReadWriteLock的原理與應(yīng)用詳解

 更新時間:2022年09月30日 14:09:10   作者:mikechen的互聯(lián)網(wǎng)架構(gòu)  
Java并發(fā)編程提供了讀寫鎖,主要用于讀多寫少的場景,今天我們就重點來講解讀寫鎖ReadWriteLock的原理與應(yīng)用場景,感興趣的可以了解一下

Java并發(fā)編程提供了讀寫鎖,主要用于讀多寫少的場景,今天我就重點來講解讀寫鎖的底層實現(xiàn)原理

什么是讀寫鎖?

讀寫鎖并不是JAVA所特有的讀寫鎖(Readers-Writer Lock)顧名思義是一把鎖分為兩部分:讀鎖和寫鎖,其中讀鎖允許多個線程同時獲得,因為讀操作本身是線程安全的,而寫鎖則是互斥鎖,不允許多個線程同時獲得寫鎖,并且寫操作和讀操作也是互斥的。

所謂的讀寫鎖(Readers-Writer Lock),顧名思義就是將一個鎖拆分為讀鎖和寫鎖兩個鎖。

其中讀鎖允許多個線程同時獲得,而寫鎖則是互斥鎖,不允許多個線程同時獲得寫鎖,并且寫操作和讀操作也是互斥的。

為什么需要讀寫鎖?

Synchronized 和 ReentrantLock 都是獨占鎖,即在同一時刻只有一個線程獲取到鎖。

然而在有些業(yè)務(wù)場景中,我們大多在讀取數(shù)據(jù),很少寫入數(shù)據(jù),這種情況下,如果仍使用獨占鎖,效率將及其低下。

針對這種情況,Java提供了讀寫鎖——ReentrantReadWriteLock。

主要解決:對共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁的場景。

讀寫鎖的特點

  • 公平性:讀寫鎖支持非公平和公平的鎖獲取方式,非公平鎖的吞吐量優(yōu)于公平鎖的吞吐量,默認構(gòu)造的是非公平鎖
  • 可重入:在線程獲取讀鎖之后能夠再次獲取讀鎖,但是不能獲取寫鎖,而線程在獲取寫鎖之后能夠再次獲取寫鎖,同時也能獲取讀鎖
  • 鎖降級:線程獲取寫鎖之后獲取讀鎖,再釋放寫鎖,這樣實現(xiàn)了寫鎖變?yōu)樽x鎖,也叫鎖降級

讀寫鎖的使用場景

ReentrantReadWriteLock適合讀多寫少的場景:

讀鎖ReentrantReadWriteLock.ReadLock可以被多個線程同時持有, 所以并發(fā)能力很高。

寫鎖ReentrantReadWriteLock.WriteLock是獨占鎖, 在一個線程持有寫鎖時候, 其他線程都不能在搶占, 包含搶占讀鎖都會阻塞。

ReentrantReadWriteLock的使用場景總結(jié):其實就是 讀讀并發(fā)、讀寫互斥、寫寫互斥而已,如果一個對象并發(fā)讀的場景大于并發(fā)寫的場景,那就可以使用 ReentrantReadWriteLock來達到保證線程安全的前提下提高并發(fā)效率。

讀寫鎖的主要成員和結(jié)構(gòu)圖

1. ReentrantReadWriteLock的繼承關(guān)系

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading.
     */
    Lock readLock();
    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing.
     */
    Lock writeLock();
}

讀寫鎖 ReadWriteLock

讀寫鎖維護了一對相關(guān)的鎖,一個用于只讀操作,一個用于寫入操作。

只要沒有寫入,讀取鎖可以由多個讀線程同時保持,寫入鎖是獨占的。

2.ReentrantReadWriteLock的核心變量

ReentrantReadWriteLock類包含三個核心變量:

  • ReaderLock:讀鎖,實現(xiàn)了Lock接口
  • WriterLock:寫鎖,也實現(xiàn)了Lock接口
  • Sync:繼承自AbstractQueuedSynchronize(AQS),可以為公平鎖FairSync 或 非公平鎖NonfairSync

3.ReentrantReadWriteLock的成員變量和構(gòu)造函數(shù)

/** 內(nèi)部提供的讀鎖 */

    private final ReentrantReadWriteLock.ReadLock readerLock;

    /** 內(nèi)部提供的寫鎖 */
    private final ReentrantReadWriteLock.WriteLock writerLock;

    /** AQS來實現(xiàn)的同步器 */
    final Sync sync;

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * 默認創(chuàng)建非公平的讀寫鎖
     */
    public ReentrantReadWriteLock() {
        this(false);
    }

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * the given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

讀寫鎖的實現(xiàn)原理

ReentrantReadWriteLock實現(xiàn)關(guān)鍵點,主要包括:

  • 讀寫狀態(tài)的設(shè)計
  • 寫鎖的獲取與釋放
  • 讀鎖的獲取與釋放
  • 鎖降級

1.讀寫狀態(tài)的設(shè)計

之前談ReentrantLock的時候,Sync類是繼承于AQS,主要以int state為線程鎖狀態(tài),0表示沒有被線程占用,1表示已經(jīng)有線程占用。

同樣ReentrantReadWriteLock也是繼承于AQS來實現(xiàn)同步,那int state怎樣同時來區(qū)分讀鎖和寫鎖的?

如果在一個整型變量上維護多種狀態(tài),就一定需要“按位切割使用”這個變量,ReentrantReadWriteLock將int類型的state將變量切割成兩部分:

  • 高16位記錄讀鎖狀態(tài)
  • 低16位記錄寫鎖狀態(tài)

abstract static class Sync extends AbstractQueuedSynchronizer {
    // 版本序列號
    private static final long serialVersionUID = 6317671515068378041L;        
    // 高16位為讀鎖,低16位為寫鎖
    static final int SHARED_SHIFT   = 16;
    // 讀鎖單位
    static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
    // 讀鎖最大數(shù)量
    static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
    // 寫鎖最大數(shù)量
    static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
    // 本地線程計數(shù)器
    private transient ThreadLocalHoldCounter readHolds;
    // 緩存的計數(shù)器
    private transient HoldCounter cachedHoldCounter;
    // 第一個讀線程
    private transient Thread firstReader = null;
    // 第一個讀線程的計數(shù)
    private transient int firstReaderHoldCount;
}

2.寫鎖的獲取與釋放

protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
            Thread current = Thread.currentThread();
            int c = getState();
            //獲取獨占鎖(寫鎖)的被獲取的數(shù)量
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                //1.如果同步狀態(tài)不為0,且寫狀態(tài)為0,則表示當前同步狀態(tài)被讀鎖獲取
                //2.或者當前擁有寫鎖的線程不是當前線程
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }

1)c是獲取當前鎖狀態(tài),w是獲取寫鎖的狀態(tài)。

2)如果鎖狀態(tài)不為零,而寫鎖的狀態(tài)為0,則表示讀鎖狀態(tài)不為0,所以當前線程不能獲取寫鎖?;蛘哝i狀態(tài)不為零,而寫鎖的狀態(tài)也不為0,但是獲取寫鎖的線程不是當前線程,則當前線程不能獲取寫鎖。

3)寫鎖是一個可重入的排它鎖,在獲取同步狀態(tài)時,增加了一個讀鎖是否存在的判斷。

寫鎖的釋放與ReentrantLock的釋放過程類似,每次釋放將寫狀態(tài)減1,直到寫狀態(tài)為0時,才表示該寫鎖被釋放了。

3.讀鎖的獲取與釋放

protected final int tryAcquireShared(int unused) {
    for(;;) {
        int c = getState();
        int nextc = c + (1<<16);
        if(nextc < c) {
           throw new Error("Maxumum lock count exceeded");
        }
        if(exclusiveCount(c)!=0 && owner != Thread.currentThread())
           return -1;
        if(compareAndSetState(c,nextc))
           return 1;
    }
}

1)讀鎖是一個支持重進入的共享鎖,可以被多個線程同時獲取。

2)在沒有寫狀態(tài)為0時,讀鎖總會被成功獲取,而所做的也只是增加讀狀態(tài)(線程安全)

3)讀狀態(tài)是所有線程獲取讀鎖次數(shù)的總和,而每個線程各自獲取讀鎖的次數(shù)只能選擇保存在ThreadLocal中,由線程自身維護。

讀鎖的每次釋放均減小狀態(tài)(線程安全的,可能有多個讀線程同時釋放鎖),減小的值是1<<16。

4.鎖降級

降級是指當前把持住寫鎖,再獲取到讀鎖,隨后釋放(先前擁有的)寫鎖的過程。

鎖降級過程中的讀鎖的獲取是否有必要,答案是必要的。主要是為了保證數(shù)據(jù)的可見性,如果當前線程不獲取讀鎖而直接釋放寫鎖,假設(shè)此刻另一個線程獲取的寫鎖,并修改了數(shù)據(jù),那么當前線程就步伐感知到線程T的數(shù)據(jù)更新,如果當前線程遵循鎖降級的步驟,那么線程T將會被阻塞,直到當前線程使數(shù)據(jù)并釋放讀鎖之后,線程T才能獲取寫鎖進行數(shù)據(jù)更新。

5.讀鎖與寫鎖的整體流程

讀寫鎖總結(jié)

本篇詳細介紹了ReentrantReadWriteLock的特征、實現(xiàn)、鎖的獲取過程,通過4個關(guān)鍵點的核心設(shè)計:

  • 讀寫狀態(tài)的設(shè)計
  • 寫鎖的獲取與釋放
  • 讀鎖的獲取與釋放
  • 鎖降級

從而才能實現(xiàn):共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁的應(yīng)用場景。

以上就是Java中讀寫鎖ReadWriteLock的原理與應(yīng)用詳解的詳細內(nèi)容,更多關(guān)于Java讀寫鎖ReadWriteLock的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java設(shè)計模式之觀察者模式解析

    Java設(shè)計模式之觀察者模式解析

    這篇文章主要介紹了Java設(shè)計模式之觀察者模式解析,觀察者模式,又被稱為發(fā)布/訂閱模式,它定義了一種一對多的依賴關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象,這個主題對象在狀態(tài)變化時,會通知所有的觀察者對象,使他們能夠自動更新自己,需要的朋友可以參考下
    2023-09-09
  • Java使用I/O流讀取文件內(nèi)容的方法詳解

    Java使用I/O流讀取文件內(nèi)容的方法詳解

    這篇文章主要介紹了Java使用I/O流讀取文件內(nèi)容的方法,結(jié)合實例形式詳細分析了java使用I/O流讀取文件常見操作技巧,需要的朋友可以參考下
    2019-11-11
  • SpringBoot開發(fā)存儲服務(wù)器實現(xiàn)過程詳解

    SpringBoot開發(fā)存儲服務(wù)器實現(xiàn)過程詳解

    這篇文章主要為大家介紹了SpringBoot開發(fā)存儲服務(wù)器實現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • spring配置不掃描service層的原因解答

    spring配置不掃描service層的原因解答

    這篇文章主要介紹了spring配置不掃描service層的原因解答,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • JAVA實現(xiàn)經(jīng)典掃雷游戲的示例代碼

    JAVA實現(xiàn)經(jīng)典掃雷游戲的示例代碼

    windows自帶的游戲《掃雷》是陪伴了無數(shù)人的經(jīng)典游戲,本程序參考《掃雷》的規(guī)則進行了簡化,用java語言實現(xiàn),采用了swing技術(shù)進行了界面化處理。感興趣的可以學(xué)習(xí)一下
    2022-01-01
  • 詳解SpringBoot之訪問靜態(tài)資源(webapp...)

    詳解SpringBoot之訪問靜態(tài)資源(webapp...)

    這篇文章主要介紹了詳解SpringBoot之訪問靜態(tài)資源(webapp...),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 將一個數(shù)組按照固定大小進行拆分成數(shù)組的方法

    將一個數(shù)組按照固定大小進行拆分成數(shù)組的方法

    下面小編就為大家?guī)硪黄獙⒁粋€數(shù)組按照固定大小進行拆分成數(shù)組的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • Java連接FTP服務(wù)器并使用ftp連接池進行文件操作指南

    Java連接FTP服務(wù)器并使用ftp連接池進行文件操作指南

    使用FTP最主要的功能是對文件進行管理,下面這篇文章主要給大家介紹了關(guān)于Java連接FTP服務(wù)器并使用ftp連接池進行文件操作的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-08-08
  • 詳解Java中native方法的使用

    詳解Java中native方法的使用

    native是與C++聯(lián)合開發(fā)的時候用的!使用native關(guān)鍵字說明這個方法是原生函數(shù),也就是這個方法是用C/C++語言實現(xiàn)的,并且被編譯成了DLL,由java去調(diào)用。本文給大家介紹java 中native方法使用,感興趣的朋友一起看看吧
    2020-09-09
  • Java發(fā)送https請求代碼實例

    Java發(fā)送https請求代碼實例

    這篇文章主要介紹了Java發(fā)送https請求代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-08-08

最新評論

远安县| 大埔县| 财经| 怀柔区| 阳东县| 海安县| 邵东县| 三原县| 泊头市| 柘城县| 三都| 红原县| 嘉峪关市| 伽师县| 肥东县| 龙海市| 漳州市| 大丰市| 苏尼特左旗| 长乐市| 克山县| 定州市| 阜宁县| 镇赉县| 邹城市| 富平县| 桂平市| 凭祥市| 金溪县| 桦川县| 同江市| 垦利县| 博野县| 龙陵县| 含山县| 寻甸| 大兴区| 武鸣县| 高淳县| 沾益县| 如皋市|