欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java底層AQS實(shí)現(xiàn)類ReentrantLock鎖的構(gòu)成及源碼解析

 更新時(shí)間:2022年10月09日 14:45:50   作者:Q.E.D  
本章我們就要來學(xué)習(xí)一下第一個(gè)?AQS?的實(shí)現(xiàn)類:ReentrantLock,看看其底層是如何組合?AQS?,實(shí)現(xiàn)了自己的那些功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步

引導(dǎo)語

本章的描述思路是先描述清楚 ReentrantLock 的構(gòu)成組件,然后使用加鎖和釋放鎖的方法把這些組件串起來。

1、類注釋

ReentrantLock 中文我們習(xí)慣叫做可重入互斥鎖,可重入的意思是同一個(gè)線程可以對(duì)同一個(gè)共享資源重復(fù)的加鎖或釋放鎖,互斥就是 AQS 中的排它鎖的意思,只允許一個(gè)線程獲得鎖。

我們來一起來看下類注釋上都有哪些重要信息:

  • 可重入互斥鎖,和 synchronized 鎖具有同樣的功能語義,但更有擴(kuò)展性;
  • 構(gòu)造器接受 fairness 的參數(shù),fairness 是 ture 時(shí),保證獲得鎖時(shí)的順序,false 不保證;
  • 公平鎖的吞吐量較低,獲得鎖的公平性不能代表線程調(diào)度的公平性;
  • tryLock() 無參方法沒有遵循公平性,是非公平的(lock 和 unlock 都有公平和非公平,而 tryLock 只有公平鎖,所以單獨(dú)拿出來說一說)。

我們補(bǔ)充一下第二點(diǎn),ReentrantLock 的公平和非公平,是針對(duì)獲得鎖來說的,如果是公平的,可以保證同步隊(duì)列中的線程從頭到尾的順序依次獲得鎖,非公平的就無法保證,在釋放鎖的過程中,我們是沒有公平和非公平的說法的。

2、類結(jié)構(gòu)

ReentrantLock 類本身是不繼承 AQS 的,實(shí)現(xiàn)了 Lock 接口,如下:

public class ReentrantLock implements Lock, java.io.Serializable {}

Lock 接口定義了各種加鎖,釋放鎖的方法,接口有如下幾個(gè):

// 獲得鎖方法,獲取不到鎖的線程會(huì)到同步隊(duì)列中阻塞排隊(duì)
void lock();
// 獲取可中斷的鎖
void lockInterruptibly() throws InterruptedException;
// 嘗試獲得鎖,如果鎖空閑,立馬返回 true,否則返回 false
boolean tryLock();
// 帶有超時(shí)等待時(shí)間的鎖,如果超時(shí)時(shí)間到了,仍然沒有獲得鎖,返回 false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 釋放鎖
void unlock();
// 得到新的 Condition
Condition newCondition();

ReentrantLock 就負(fù)責(zé)實(shí)現(xiàn)這些接口,我們使用時(shí),直接面對(duì)的也是這些方法,這些方法的底層實(shí)現(xiàn)都是交給 Sync 內(nèi)部類去實(shí)現(xiàn)的,Sync 類的定義如下:

abstract static class Sync extends AbstractQueuedSynchronizer {}

Sync 繼承了 AbstractQueuedSynchronizer ,所以 Sync 就具有了鎖的框架,根據(jù) AQS 的框架,Sync 只需要實(shí)現(xiàn) AQS 預(yù)留的幾個(gè)方法即可,但 Sync 也只是實(shí)現(xiàn)了部分方法,還有一些交給子類 NonfairSync 和 FairSync 去實(shí)現(xiàn)了,NonfairSync 是非公平鎖,F(xiàn)airSync 是公平鎖,定義如下:

// 同步器 Sync 的兩個(gè)子類鎖
static final class FairSync extends Sync {}
static final class NonfairSync extends Sync {}

幾個(gè)類整體的結(jié)構(gòu)如下:

圖片描述

圖中 Sync、NonfairSync、FairSync 都是靜態(tài)內(nèi)部類的方式實(shí)現(xiàn)的,這個(gè)也符合 AQS 框架定義的實(shí)現(xiàn)標(biāo)準(zhǔn)。

3、構(gòu)造器 

ReentrantLock 構(gòu)造器有兩種,代碼如下:

// 無參數(shù)構(gòu)造器,相當(dāng)于 ReentrantLock(false),默認(rèn)是非公平的
public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

無參構(gòu)造器默認(rèn)構(gòu)造是非公平的鎖,有參構(gòu)造器可以選擇。

從構(gòu)造器中可以看出,公平鎖是依靠 FairSync 實(shí)現(xiàn)的,非公平鎖是依靠 NonfairSync 實(shí)現(xiàn)的。

4、Sync 同步器

Sync 表示同步器,繼承了 AQS,UML 圖如下:

圖片描述

從 UML 圖中可以看出,lock 方法是個(gè)抽象方法,留給 FairSync 和 NonfairSync 兩個(gè)子類去實(shí)現(xiàn),我們一起來看下剩余重要的幾個(gè)方法。

4.1、nonfairTryAcquire 

// 嘗試獲得非公平鎖
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    // 同步器的狀態(tài)是 0,表示同步器的鎖沒有人持有
    if (c == 0) {
        // 當(dāng)前線程持有鎖
        if (compareAndSetState(0, acquires)) {
            // 標(biāo)記當(dāng)前持有鎖的線程是誰
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 如果當(dāng)前線程已經(jīng)持有鎖了,同一個(gè)線程可以對(duì)同一個(gè)資源重復(fù)加鎖,代碼實(shí)現(xiàn)的是可重入鎖
    else if (current == getExclusiveOwnerThread()) {
        // 當(dāng)前線程持有鎖的數(shù)量 + acquires
        int nextc = c + acquires;
        // int 是有最大值的,<0 表示持有鎖的數(shù)量超過了 int 的最大值
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //否則線程進(jìn)入同步隊(duì)列
    return false;
}

以上代碼有三點(diǎn)需要注意:

通過判斷 AQS 的 state 的狀態(tài)來決定是否可以獲得鎖,0 表示鎖是空閑的;else if 的代碼體現(xiàn)了可重入加鎖,同一個(gè)線程對(duì)共享資源重入加鎖,底層實(shí)現(xiàn)就是把 state + 1,并且可重入的次數(shù)是有限制的,為 Integer 的最大值;這個(gè)方法是非公平的,所以只有非公平鎖才會(huì)用到,公平鎖是另外的實(shí)現(xiàn)。

無參的 tryLock 方法調(diào)用的就是此方法,tryLock 的方法源碼如下:

public boolean tryLock() {
    // 入?yún)?shù)是 1 表示嘗試獲得一次鎖
    return sync.nonfairTryAcquire(1);
}

4.2、tryRelease

// 釋放鎖方法,非公平和公平鎖都使用
protected final boolean tryRelease(int releases) {
    // 當(dāng)前同步器的狀態(tài)減去釋放的個(gè)數(shù),releases 一般為 1
    int c = getState() - releases;
    // 當(dāng)前線程根本都不持有鎖,報(bào)錯(cuò)
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 如果 c 為 0,表示當(dāng)前線程持有的鎖都釋放了
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    // 如果 c 不為 0,那么就是可重入鎖,并且鎖沒有釋放完,用 state 減去 releases 即可,無需做其他操作
    setState(c);
    return free;
}

tryRelease 方法是公平鎖和非公平鎖都公用的,在鎖釋放的時(shí)候,是沒有公平和非公平的說法的。

從代碼中可以看到,鎖最終被釋放的標(biāo)椎是 state 的狀態(tài)為 0,在重入加鎖的情況下,需要重入解鎖相應(yīng)的次數(shù)后,才能最終把鎖釋放,比如線程 A 對(duì)共享資源 B 重入加鎖 5 次,那么釋放鎖的話,也需要釋放 5 次之后,才算真正的釋放該共享資源了。

5、FairSync 公平鎖

FairSync 公平鎖只實(shí)現(xiàn)了 lock 和 tryAcquire 兩個(gè)方法,lock 方法非常簡(jiǎn)單,如下:

// acquire 是 AQS 的方法,表示先嘗試獲得鎖,失敗之后進(jìn)入同步隊(duì)列阻塞等待
final void lock() {
    acquire(1);
}

tryAcquire 方法是 AQS 在 acquire 方法中留給子類實(shí)現(xiàn)的抽象方法,F(xiàn)airSync 中實(shí)現(xiàn)的源碼如下:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // hasQueuedPredecessors 是實(shí)現(xiàn)公平的關(guān)鍵
        // 會(huì)判斷當(dāng)前線程是不是屬于同步隊(duì)列的頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)(頭節(jié)點(diǎn)是釋放鎖的節(jié)點(diǎn))
        // 如果是(返回false),符合先進(jìn)先出的原則,可以獲得鎖
        // 如果不是(返回true),則繼續(xù)等待
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 可重入鎖
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

代碼和 Sync 的 nonfairTryAcquire 方法實(shí)現(xiàn)類似,唯一不同的是在獲得鎖時(shí)使用 hasQueuedPredecessors 方法體現(xiàn)了其公平性。

6、NonfairSync 非公平鎖

NonfairSync 底層實(shí)現(xiàn)了 lock 和 tryAcquire 兩個(gè)方法,如下:

// 加鎖
final void lock() {
    // cas 給 state 賦值
    if (compareAndSetState(0, 1))
        // cas 賦值成功,代表拿到當(dāng)前鎖,記錄拿到鎖的線程
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // acquire 是抽象類AQS的方法,
        // 會(huì)再次嘗試獲得鎖,失敗會(huì)進(jìn)入到同步隊(duì)列中
        acquire(1);
}
// 直接使用的是 Sync.nonfairTryAcquire 方法 
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

7、如何串起來

以上內(nèi)容主要說了 ReentrantLock 的基本結(jié)構(gòu),比較零散,那么這些零散的結(jié)構(gòu)如何串聯(lián)起來呢?我們是通過 lock、tryLock、unlock 這三個(gè) API 將以上幾個(gè)類串聯(lián)起來,我們來一一看下。

7.1 lock 加鎖

lock 的代碼實(shí)現(xiàn):

public void lock() {
    sync.lock();
}

其底層的調(diào)用關(guān)系(只是簡(jiǎn)單表明調(diào)用關(guān)系,并不是完整分支圖)如下:

圖片描述

7.2 tryLock 嘗試加鎖 

 tryLock 有兩個(gè)方法,一種是無參的,一種提供了超時(shí)時(shí)間的入?yún)ⅲ瑑煞N內(nèi)部是不同的實(shí)現(xiàn)機(jī)制,代碼分別如下:

// 無參構(gòu)造器
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
// timeout 為超時(shí)的時(shí)間,在時(shí)間內(nèi),仍沒有得到鎖,會(huì)返回 false
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

接著我們一起看下兩個(gè) tryLock 的調(diào)用關(guān)系圖,下圖顯示的是無參 tryLock 的調(diào)用關(guān)系圖,如下:

圖片描述

我們需要注意的是 tryLock 無參方法底層走的就是非公平鎖實(shí)現(xiàn),沒有公平鎖的實(shí)現(xiàn)。

下圖展示的是帶有超時(shí)時(shí)間的有參 tryLock 的調(diào)用實(shí)現(xiàn)圖:

圖片描述

7.3 unlock 釋放鎖 

unlock 釋放鎖的方法,底層調(diào)用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成兩步:

嘗試釋放鎖,如果釋放失敗,直接返回 false;釋放成功,從同步隊(duì)列的頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)開始喚醒,讓其去競(jìng)爭(zhēng)鎖。

第一步就是我們上文中 Sync 的 tryRelease 方法(4.1),第二步 AQS 已經(jīng)實(shí)現(xiàn)了。

unLock 的源碼如下:

// 釋放鎖
public void unlock() {
    sync.release(1);
}

7.4 Condition

ReentrantLock 對(duì) Condition 并沒有改造,直接使用 AQS 的 ConditionObject 即可。

8、總結(jié)

這就是我們?cè)谘芯客?AQS 源碼之后,碰到的第一個(gè)鎖,是不是感覺很簡(jiǎn)單,AQS 搭建了整個(gè)鎖架構(gòu),子類鎖只需要根據(jù)場(chǎng)景,實(shí)現(xiàn) AQS 對(duì)應(yīng)的方法即可,不僅僅是 ReentrantLock 是這樣,JUC 中的其它鎖也都是這樣,只要對(duì) AQS 了如指掌,鎖其實(shí)非常簡(jiǎn)單。

以上就是java底層AQS實(shí)現(xiàn)類kReentrantLock鎖的構(gòu)成及源碼解析的詳細(xì)內(nèi)容,更多關(guān)于AQS實(shí)現(xiàn)類ReentrantLock鎖的構(gòu)成的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot整合Keycloak實(shí)現(xiàn)單點(diǎn)登錄的示例代碼

    SpringBoot整合Keycloak實(shí)現(xiàn)單點(diǎn)登錄的示例代碼

    本文主要介紹了SpringBoot整合Keycloak實(shí)現(xiàn)單點(diǎn)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Java設(shè)計(jì)模式中的原型模式講解

    Java設(shè)計(jì)模式中的原型模式講解

    原型模式是用于創(chuàng)建重復(fù)的對(duì)象,同時(shí)又能保證性能。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式,今天通過本文給大家介紹下Java?原型設(shè)計(jì)模式,感興趣的朋友一起看看吧
    2023-04-04
  • Java中如何獲取文件的上級(jí)目錄

    Java中如何獲取文件的上級(jí)目錄

    這篇文章主要介紹了Java中如何獲取文件的上級(jí)目錄問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 通過實(shí)例講解springboot整合WebSocket

    通過實(shí)例講解springboot整合WebSocket

    這篇文章主要介紹了通過實(shí)例講解springboot整合WebSocket,WebSocket為游覽器和服務(wù)器提供了雙工異步通信的功能,即游覽器可以向服務(wù)器發(fā)送消息,服務(wù)器也可以向游覽器發(fā)送消息。,需要的朋友可以參考下
    2019-06-06
  • 最新評(píng)論