日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一文理解什么是讀寫鎖分離?如何實現讀寫鎖?

 

場景描述

相信很多開發者都遇到過多線程并發訪問共享資源的情況,那么為了防止在多線程訪問的時候出現線程安全問題,通常我們可以使用加鎖的方式來避免線程安全問題,可以使用synchronized關鍵字以及一些顯式鎖操作,當然在分布式場景中還可以使用一些分布式鎖機制來保證共享資源線程安全訪問。

對于共享資源的訪問,可以分為兩種操作,一種是對共享資源的讀操作,一種是對共享資源的寫操作。如果有多個線程在同一時刻對共享資源的操作都是讀操作的話,雖然是存在資源競爭的情況,但是并不會影響到最終的訪問結果,也不會引起數據不一致的情況發生。這樣時候,如果還是使用排他鎖的方式進行加鎖,很明顯就有點得不償失了。如下圖所示,共享資源被多個線程共同進行讀操作的時候,是不會出現線程安全問題的。

一文理解什么是讀寫鎖分離?如何實現讀寫鎖?

 

所以,如果在某個場景中,對于共享資源的讀操作要明顯多于寫操作的時候,這個時候,讀操作是可以不用進行加鎖的。這樣對系統的性能的提升也會非常明顯。

如圖所示,在多個線程對共享資源進行寫操作的時候,很明顯,一定會造成資源的數據一致性問題出現。所以對于寫操作來講,一定要注意進行加鎖的操作。

讀寫分離程序實現

第一步、定義一個鎖接口

無論是對于讀鎖還是寫鎖,都離不開加鎖和解鎖兩個操作,所以在接口中需要定義兩個基本的操作,加鎖和解鎖,代碼如下。

public interface Lock {
    /**
     * 進行加鎖操作
     * @throws InterruptedException
     */
    void lock() throws InterruptedException;

    /**
     * 進行解鎖操作
     */
    void unlock();
}

第二步、讀寫鎖接口實現

在定義好鎖接口之后,接下來就是需要去定義如何來操作鎖。既然需要對讀寫鎖進行判斷,那么首先就需要有關于鎖的判斷條件的規范,下面實現的ReadWriteLock其實就是讀寫鎖的使用規范。

public interface ReadWriteLock {
    /**
     * 創建讀鎖
     * @return
     */
    Lock readLock();
    /**
     * 創建寫鎖
     * @return
     */
    Lock writeLock();
    /**
     * 獲取正在執行的寫操作個數
     * @return
     */
    int getDoingWriters();
    /**
     * 獲取正在等待執行的寫操作個數
     * @return
     */
    int getWAItingWriters();
    /**
     * 獲取正在執行的讀操作個數
     * @return
     */
    int getDoingReader();


    static ReadWriteLock readWriteLock(){
        return new ReadWriteLockImpl();
    }

    static ReadWriteLock readWriteLock(boolean preferWriter){
        return new ReadWriteLockImpl(preferWriter);
    }

}

會看到,在上面這個接口中定義了如下的一些操作

  • 創建讀鎖:readLock()
  • 創建寫鎖:writeLock()
  • 獲取正在執行的讀操作:getDoingReader()
  • 獲取正在執行的寫操作:getDoingWriters()
  • 獲取等待執行的寫操作:getWaitingWriters()

為什么要有這些操作呢?其實這樣寫操作就是來規定如何去使用讀寫鎖的,例如,讀操作的個數大于0的時候,這個適合就意味著寫操作的個數是等于0的。反之當寫操作的個數大于0 的時候,就意味著讀操作的個數是等于0的。因為在讀寫操作的過程中,讀操作和寫操作是沖突的過程,也就是說在寫的時候不能讀,在讀的時候不能寫。那么通過這樣的一個數量關系我們就可以實現什么時候加鎖什么時候解鎖了。

第三步、按照規則實現讀寫鎖。

既然有了上面的規則的定義,那么一定要有規則的實現,下面這段代碼其實就是對上面的規則的實現。

public class ReadWriteLockImpl implements ReadWriteLock {
    /**
     * 定義鎖對象
     */
    private final Object MUTEX = new Object();

    private int doingWriters = 0;

    private int waitingWriters = 0;

    private int doingReaders = 0;
    /**
     * 偏好設置
     */
    private boolean preferWriter;
    public ReadWriteLockImpl() {
        this(true);
    }
    public ReadWriteLockImpl(boolean preferWriter) {
        this.preferWriter = preferWriter;
    }
    @Override
    public Lock readLock() {
        return new ReadLock(this);
    }
    @Override
    public Lock writeLock() {
        return new WriteLock(this);
    }
    void addDoingWriters(){
        this.doingWriters++;
    }
    void addWaitingWriters(){
        this.waitingWriters++;
    }
    void addDoingReader(){
        this.doingReaders++;
    }
    void minusDoingWriters(){
        this.doingWriters--;
    }
    void minusWaitingWriters(){
        this.waitingWriters--;
    }
    void  minusDoingReader(){
        this.doingReaders--;
    }
    @Override
    public int getDoingWriters() {
        return this.doingWriters;
    }
    @Override
    public int getWaitingWriters() {
        return this.waitingWriters;
    }
    @Override
    public int getDoingReader() {
        return this.doingReaders;
    }
    Object getMutex(){
        return this.MUTEX;
    }
    boolean getPreferWriter(){
        return this.preferWriter;
    }
    void changePrefer(boolean preferWriter){
        this.preferWriter = preferWriter;
    }
}

上述代碼中包含了如下的一些內容

  • addDoingWriters() :正在執行的寫操作加一;
  • addWaitingWriters():等待執行的寫操作加一;
  • addDoingReader():正在執行的讀操作加一;
  • minusDoingWriters():正在執行的寫操作減一;
  • minusWaitingWriters():等待執行的寫操作減一;
  • minusDoingReader():正在執行的讀操作減一;
  • changePrefer(boolean preferWriter):修改偏好設置;
  • Object getMutex():獲取鎖對象;

其他的操作可以先不做了解,這里重要的部分有兩部分內容,第一是對于等待讀寫操作的增加和減少;第二則是對Object getMutex()操作的立即。

對于讀寫操作數量的判斷主要是用來進行加鎖和解鎖操作的判斷。那么Object getMutex()是用來干什么的?

Object getMutex()操作是用來獲取一個鎖對象,那么我們真正使用的ReadLock 和 WriterLock又是干嘛的?其實細心的讀者可能發現了Object getMutex()鎖操作其實是為了保證讀寫操作內部的一個線程安全,也就是為了保證我們對于加鎖條件判斷的一個線程安全性的保證,而真正我們通過readLock()方法和writeLock()方法進行的讀寫操作實現才是讀寫鎖的重點。

實現讀鎖

根據上面的規則,讀鎖的條件是當前沒有再執行的寫操作的時候就可以進行加鎖,當前沒有再有執行的讀操作的時候就可以釋放鎖。根據規則實現,代碼如下。

public class ReadLock implements Lock {

    private final ReadWriteLockImpl readWriteLock;

    public ReadLock(ReadWriteLockImpl readWriteLock) {
        this.readWriteLock = readWriteLock;
    }
  /***
  * 進行加鎖操作
  */
    @Override
    public void lock() throws InterruptedException {
        synchronized (readWriteLock.getMutex()){
            while (readWriteLock.getDoingWriters()>0
                   ||(readWriteLock.getPreferWriter()
                      &&readWriteLock.getWaitingWriters()>0)){
                readWriteLock.getMutex().wait();
            }
            readWriteLock.addDoingReader();
        }
    }

		/**
		* 進行解鎖操作
		*/
    @Override
    public void unlock() {
        synchronized (readWriteLock.getMutex()){
            readWriteLock.minusDoingReader();
            readWriteLock.changePrefer(true);
            readWriteLock.getMutex().notifyAll();
        }
    }
}

實現寫鎖

寫鎖實現的條件根據上面的描述可以知道,加鎖的條件是當前沒有讀操作,解鎖的條件是當前沒有正在執行的寫操作。代碼實現如下。

public class WriteLock implements Lock {

    private final ReadWriteLockImpl readWriteLock;

    public WriteLock(ReadWriteLockImpl readWriteLock) {
        this.readWriteLock = readWriteLock;
    }

    /**
     * 進行加鎖操作
     * @throws InterruptedException
     */
    @Override
    public void lock() throws InterruptedException {

        synchronized (readWriteLock.getMutex()){
            try{
                readWriteLock.addWaitingWriters();
                while (readWriteLock.getDoingReader()>0
                       ||readWriteLock.getDoingWriters()>0){
                    readWriteLock.getMutex().wait();
                }
            }finally {
                this.readWriteLock.minusWaitingWriters();
            }
            readWriteLock.addDoingWriters();
        }
    }

    /**
     * 進行解鎖操作
     */
    @Override
    public void unlock() {
        synchronized (readWriteLock.getMutex()){
            readWriteLock.minusDoingWriters();
            readWriteLock.changePrefer(true);
            readWriteLock.getMutex().notifyAll();
        }
    }
}

分析

根據讀寫鎖分別的實現來看,似乎底層鎖定的就是在ReadWriteLockImpl規則中實現的final Object MUTEX = new Object()鎖定對象,但是仔細想來,這個對象其實就是為了保證讀寫鎖的安全性,真正能夠行決定讀寫鎖的其實是ReadWriteLockImpl對象中定義一些數量規則。通過這些數量規則的判斷決定是讀操作還是寫操作。

另外在JDK并發包
JAVA.util.concurrent.locks中提供了一些讀寫鎖的操作,如下圖所示。

一文理解什么是讀寫鎖分離?如何實現讀寫鎖?

 

通過圖中所提供的方法來看,基本的實現思路與上面我們的實現思路是一樣。我們采取的是偏向設置,而JDK提供的是公平性相關的內容。而關于公平性相關的內容,這里我們不做過多的介紹,有興趣的讀者可以自己研究一下啊。

分享到:
標簽:讀寫
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定