真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JUC的ReentrantLock怎么使用

這篇文章主要講解了“JUC的ReentrantLock怎么使用”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“JUC的ReentrantLock怎么使用”吧!

創(chuàng)新互聯(lián)主要業(yè)務(wù)有網(wǎng)站營銷策劃、網(wǎng)站制作、做網(wǎng)站、微信公眾號開發(fā)、小程序設(shè)計、H5場景定制、程序開發(fā)等業(yè)務(wù)。一次合作終身朋友,是我們奉行的宗旨;我們不僅僅把客戶當客戶,還把客戶視為我們的合作伙伴,在開展業(yè)務(wù)的過程中,公司還積累了豐富的行業(yè)經(jīng)驗、網(wǎng)絡(luò)營銷推廣資源和合作伙伴關(guān)系資源,并逐漸建立起規(guī)范的客戶服務(wù)和保障體系。 

ReentrantLock 譯為可重入鎖,我們在使用時總是將其與 synchronized 關(guān)鍵字進行對比,實際上 ReentrantLock 與 synchronized 關(guān)鍵字在使用上具備相同的語義,區(qū)別僅在于 ReentrantLock 相對于 synchronized 關(guān)鍵字留給開發(fā)者的可操作性更強,所以在使用上更加靈活,當然凡事都有兩面,靈活的背后也暗藏著更加容易出錯的風險。

盡管語義相同,但 ReentrantLock 和 synchronized 關(guān)鍵字背后的實現(xiàn)機制卻大相徑庭。前面的文章中我們分析了 synchronized 關(guān)鍵字的實現(xiàn)內(nèi)幕,知道了 synchronized 關(guān)鍵字背后依賴于 monitor 技術(shù),而本文所要分析的 ReentrantLock 在實現(xiàn)上則依賴于 AQS 隊列同步器,具體如何基于 AQS 進行實現(xiàn),下面來一探究竟。

ReentrantLock 示例

本小節(jié)使用 ReentrantLock 實現(xiàn)一個 3 線程交替打印的程序,演示基于 ReentrantLock 實現(xiàn)鎖的獲取、釋放,以及線程之間的通知機制。示例實現(xiàn)如下:

private static Lock lock = new ReentrantLock(true);

private static Condition ca = lock.newCondition();
private static Condition cb = lock.newCondition();
private static Condition cc = lock.newCondition();

private static volatile int idx = 0;

private static class A implements Runnable {

    @Override
    public void run() {
        try {
            lock.lock();
            for (int i = 0; i < 10; i++) {
                cb.signalAll();
                System.out.println("a: " + (++idx));
                ca.await();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

private static class B implements Runnable {

    @Override
    public void run() {
        try {
            lock.lock();
            for (int i = 0; i < 10; i++) {
                cc.signalAll();
                System.out.println("b: " + (++idx));
                cb.await();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

private static class C implements Runnable {

    @Override
    public void run() {
        try {
            lock.lock();
            for (int i = 0; i < 10; i++) {
                ca.signalAll();
                System.out.println("c: " + (++idx));
                cc.await();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public static void main(String[] args) {
    new Thread(new A()).start();
    new Thread(new B()).start();
    new Thread(new C()).start();
}

上述示例定義了 3 個線程類 A、B 和 C,并按照 A -> B -> C 的順序進行組織,各個線程在調(diào)用 Lock#lock 方法獲取到鎖之后會先嘗試通知后繼線程(將對應(yīng)的線程移入到同步隊列),然后對 idx 變量進行累加并打印,接著進入等待狀態(tài)并釋放資源,方法 Lock#unlock 接下來會調(diào)度位于同步隊列隊頭結(jié)點的線程繼續(xù)執(zhí)行。

ReentrantLock 實現(xiàn)內(nèi)幕

Lock 接口

ReentrantLock 實現(xiàn)了 Lock 接口,該接口抽象了鎖應(yīng)該具備的基本操作,包括鎖資源的獲取、釋放,以及創(chuàng)建條件對象。除了本文介紹的 ReentrantLock 外,JUC 中直接或間接實現(xiàn)了 Lock 接口的組件還包括 ReentrantReadWriteLock 和 StampedLock,我們將在后面的文章中對這些組件逐一分析。Lock 接口的定義如下:

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

各方法釋義如下:

  • lock():獲取鎖資源,如果獲取失敗則阻塞。

  • lockInterruptibly():獲取鎖資源,如果獲取失敗則阻塞,阻塞期間支持響應(yīng)中斷請求。

  • tryLock():嘗試獲取鎖資源,不管是否獲取成功都立即返回,如果獲取成功則返回 true,否則返回 false。

  • tryLock(long time, TimeUnit unit):嘗試獲取鎖資源,相對于無參版本的 tryLock 方法引入了超時機制,并支持在等待期間響應(yīng)中斷請求。

  • unlock():釋放鎖資源。

  • newCondition():創(chuàng)建一個綁定到當前 Lock 上的條件對象。

資源的獲取與釋放

上一小節(jié)分析了 Lock 接口的定義,ReentrantLock 實現(xiàn)了該接口,并將接口方法的實現(xiàn)都委托給了 Sync 內(nèi)部類處理。Sync 是一個抽象類,繼承自 AbstractQueuedSynchronizer,并派生出 FairSync 和 NonfairSync 兩個子類(繼承關(guān)系如下圖),由命名可以看出 FairSync 實現(xiàn)了公平鎖,而 NonfairSync 則實現(xiàn)了非公平鎖。

JUC的ReentrantLock怎么使用

ReentrantLock 提供了帶 boolean 參數(shù)的構(gòu)造方法,依據(jù)該參數(shù)來決定是創(chuàng)建公平鎖還是非公平鎖(默認為非公平鎖),構(gòu)造方法定義如下:

public ReentrantLock() {
    // 默認創(chuàng)建非公平鎖
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    // 依據(jù)參數(shù)決定創(chuàng)建公平鎖還是非公平鎖
    sync = fair ? new FairSync() : new NonfairSync();
}

下面將區(qū)分公平鎖和非公平鎖分析 ReentrantLock 針對 Lock 接口方法的具體實現(xiàn),在開始之前先介紹一下 AQS 中的 state 字段在 ReentrantLock 中的作用。

我們知道 ReentrantLock 是可重入的,這里的可重入是指當一個線程獲取到 ReentrantLock 鎖之后,如果該線程再次嘗試獲取該 ReentrantLock 鎖時仍然可以獲取成功,對應(yīng)的重入次數(shù)加 1。ReentrantLock 的重入次數(shù)則由 AQS 的 state 字段進行記錄。當 state 為 0 時,說明目標 ReentrantLock 鎖當前未被任何線程持有,當一個線程釋放 ReentrantLock 鎖時,對應(yīng)的 state 值需要減 1。

非公平鎖

本小節(jié)我們來分析一下非公平鎖 NonfairSync 的實現(xiàn)機制,首先來看一下 NonfairSync#lock 方法,該方法用于獲取資源,如果獲取失敗則會將當前線程加入到同步隊列中阻塞等待。方法實現(xiàn)如下:

final void lock() {
    // 嘗試獲取鎖,將 state 由 0 設(shè)置為 1
    if (this.compareAndSetState(0, 1)) {
        // 首次獲取鎖成功,記錄當前鎖對象
        this.setExclusiveOwnerThread(Thread.currentThread());
    } else {
        // 目標鎖對象已經(jīng)被占用,或者非首次獲取目標鎖對象
        this.acquire(1);
    }
}

方法 NonfairSync#lock 加鎖的過程首先會基于 CAS 操作嘗試將 ReentrantLock 的 state 值由 0 改為 1,搶占鎖資源,這也是非公平語義的根本所在。如果操作成功,則說明目標 ReentrantLock 鎖當前未被任何線程持有,且本次加鎖成功。如果操作失敗則區(qū)分兩種情況:

  • 目標 ReentrantLock 鎖已被當前線程持有。

  • 目標 ReentrantLock 鎖已被其它線程持有。

針對這兩種情況,接下來會調(diào)用 AbstractQueuedSynchronizer#acquire 方法嘗試獲取 1 個單位的資源,該方法由 AQS 實現(xiàn),我們已經(jīng)在前面的文章中分析過,其中會執(zhí)行模板方法 AbstractQueuedSynchronizer#tryAcquire。NonfairSync 針對該模板方法的實現(xiàn)如下:

protected final boolean tryAcquire(int acquires) {
    return this.nonfairTryAcquire(acquires);
}

上述方法將嘗試獲取資源的邏輯委托給 Sync#nonfairTryAcquire 方法執(zhí)行,ReentrantLock 的 ReentrantLock#tryLock() 方法同樣基于該方法實現(xiàn)。下面來分析一下該方法的執(zhí)行邏輯,實現(xiàn)如下:

final boolean nonfairTryAcquire(int acquires) {
    // 獲取當前線程對象
    final Thread current = Thread.currentThread();
    // 獲取 state 值
    int c = this.getState();
    if (c == 0) {
        // state 為 0,表示目標鎖當前未被持有,嘗試獲取鎖
        if (this.compareAndSetState(0, acquires)) {
            this.setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果當前已經(jīng)持有鎖的線程已經(jīng)是當前線程
    else if (current == this.getExclusiveOwnerThread()) {
        // 重入次數(shù)加 1
        int nextc = c + acquires;
        if (nextc < 0) {
            // 重入次數(shù)溢出
            throw new Error("Maximum lock count exceeded");
        }
        // 更新 state 記錄的重入次數(shù)
        this.setState(nextc);
        return true;
    }
    // 已經(jīng)持有鎖的線程不是當前線程,嘗試加鎖失敗
    return false;
}

方法 Sync#nonfairTryAcquire 的執(zhí)行流程可以概括為;

  1. 獲取當前 ReentrantLock 鎖的 state 值;

  2. 如果 state 值為 0,說明當前 ReentrantLock 鎖未被任何線程持有,基于 CAS 嘗試將 state 值由 0 改為 1,搶占鎖資源,修改成功即為加鎖成功;

  3. 否則,如果當前已經(jīng)持有該 ReentrantLock 鎖的線程是自己,則修改重入次數(shù)(即將 state 值加 1);

  4. 否則,目標 ReentrantLock 鎖已經(jīng)被其它線程持有,加鎖失敗。

如果 Sync#nonfairTryAcquire 方法返回 false,則說明當前線程嘗試獲取目標 ReentrantLock 鎖失敗,對于 ReentrantLock#lock 方法而言,接下去線程會被加入到同步隊列阻塞等待,而對于 ReentrantLock#tryLock() 方法而言,線程會立即退出,并返回 false。

方法 ReentrantLock#newCondition 同樣是委托給 Sync#newCondition 方法處理,該方法只是簡單的創(chuàng)建了一個 ConditionObject 對象,即新建了一個條件隊列。非公平鎖 NonfairSync 中的以下方法都是直接委托給 AQS 處理,這些方法的實現(xiàn)機制已在前面分析 AQS 時介紹過:

  • ReentrantLock#lockInterruptibly:直接委托給 AbstractQueuedSynchronizer#acquireInterruptibly 方法實現(xiàn),獲取的資源數(shù)為 1。

  • ReentrantLock#tryLock(long, java.util.concurrent.TimeUnit):直接委托給 AbstractQueuedSynchronizer#tryAcquireNanos 方法實現(xiàn),獲取的資源數(shù)為 1。

  • ReentrantLock#unlock:直接委托給 AbstractQueuedSynchronizer#release 方法實現(xiàn),釋放的資源數(shù)為 1。

前面的文章,我們在分析 AQS 的 AbstractQueuedSynchronizer#release 方法時,曾介紹過該方法會調(diào)用模板方法 AbstractQueuedSynchronizer#tryRelease 以嘗試釋放資源。ReentrantLock 針對該模板方法的實現(xiàn)位于 Sync 抽象類中,所以它是一個由 NonfairSync 和 FairSync 共用的方法,下面來分析一下該方法的實現(xiàn)。

protected final boolean tryRelease(int releases) {
    // 將當前 state 記錄的重入次數(shù)減 1
    int c = this.getState() - releases;
    // 如果當前持有鎖的線程對象不是當前線程則拋出異常
    if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
        throw new IllegalMonitorStateException();
    }
    boolean free = false;
    // 如果重入次數(shù)已經(jīng)降為 0,則清空持有當前鎖的線程對象
    if (c == 0) {
        free = true;
        this.setExclusiveOwnerThread(null);
    }
    // 更新當前鎖的重入次數(shù)
    this.setState(c);
    return free;
}

嘗試釋放資源的過程本質(zhì)上就是修改 state 字段值的過程,如果當前操作的線程是持有 ReentrantLock 鎖的線程,則上述方法會將 state 值減 1,即將已重入次數(shù)減 1。如果修改后的 state 字段值為 0,則說明當前線程已經(jīng)釋放了持有的 ReentrantLock 鎖,此時需要清除記錄在 ReentrantLock 對象中的線程 Thread 對象。

公平鎖

本小節(jié)我們來分析一下公平鎖 FairSync 的實現(xiàn)機制,這里的公平本質(zhì)上是指公平的獲取鎖資源,所以主要的區(qū)別體現(xiàn)在加鎖的過程,即 ReentrantLock#lock 方法。

前面我們在分析 NonfairSync 時看到,NonfairSync 在加鎖時首先會基于 CAS 嘗試將 state 值由 0 改為 1,失敗的情況下才會繼續(xù)調(diào)用 AbstractQueuedSynchronizer#acquire 方法等待獲取資源,并且在同步隊列中等待期間仍然會在 state 為 0 時搶占獲取鎖資源。

FairSync 相對于 NonfairSync 的區(qū)別在于當 state 值為 0 時,即目標 ReentrantLock 鎖此時未被任何線程持有的情況下,F(xiàn)airSync 并不會去搶占鎖資源,而是檢查同步隊列中是否有排在前面等待獲取鎖資源的其它線程,如果有則讓渡這些排在前面的線程優(yōu)先獲取鎖資源。

下面來看一下 FairSync#lock 方法的實現(xiàn),該方法只是簡單的將獲取鎖資源操作委托給 AQS 的 AbstractQueuedSynchronizer#acquire 方法執(zhí)行,所以我們需要重點關(guān)注一下模板方法 FairSync#tryAcquire 的實現(xiàn):

protected final boolean tryAcquire(int acquires) {
        // 獲取當前線程對象
        final Thread current = Thread.currentThread();
        // 獲取當前 state 值
        int c = this.getState();
        if (c == 0) {
            // state 為 0,表示目標鎖當前未被持有,先檢查是否有阻塞等待當前鎖的線程,如果沒有再嘗試獲取鎖
            if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, acquires)) {
                this.setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 如果當前已經(jīng)持有鎖的線程已經(jīng)是當前線程,則修改已重入次數(shù)加 1
        else if (current == this.getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0) {
                throw new Error("Maximum lock count exceeded");
            }
            this.setState(nextc);
            return true;
        }
        return false;
    }
}

上述方法的執(zhí)行流程與 NonfairSync 中的相關(guān)實現(xiàn)大同小異,主要區(qū)別在于當 state 值為 0 時,F(xiàn)airSync 會調(diào)用 AbstractQueuedSynchronizer#hasQueuedPredecessors 檢查當前同步隊列中是否還有等待獲取鎖資源的其它線程,如果存在則優(yōu)先讓這些線程獲取鎖資源,并將自己加入到同步隊列中排隊等待。

感謝各位的閱讀,以上就是“JUC的ReentrantLock怎么使用”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對JUC的ReentrantLock怎么使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!


網(wǎng)頁名稱:JUC的ReentrantLock怎么使用
網(wǎng)頁鏈接:http://weahome.cn/article/pcjsdd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部