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

深入剖析Java ReentrantLock的源碼

 更新時(shí)間:2022年11月18日 16:39:47   作者:一燈架構(gòu)  
ReentrantLock和Synchronized都是Java開(kāi)發(fā)中最常用的鎖,與Synchronized這種JVM內(nèi)置鎖不同的是,ReentrantLock提供了更豐富的語(yǔ)義。本文就來(lái)深入剖析一下ReentrantLock源碼,需要的可以參考一下

ReentrantLock和Synchronized都是Java開(kāi)發(fā)中最常用的鎖,與Synchronized這種JVM內(nèi)置鎖不同的是,ReentrantLock提供了更豐富的語(yǔ)義。可以創(chuàng)建公平鎖或非公平鎖、響應(yīng)中斷、超時(shí)等待、按條件喚醒等。在某些場(chǎng)景下,使用ReentrantLock更適合,功能更強(qiáng)大。

前兩篇文章,我們分析了AQS的加鎖流程、以及源碼實(shí)現(xiàn)。當(dāng)時(shí)我們就說(shuō)了,AQS使用了模板設(shè)計(jì)模式,父類中定義加鎖流程,子類去實(shí)現(xiàn)具體的加鎖邏輯。所以大部分加鎖代碼已經(jīng)在父類AQS中實(shí)現(xiàn)了,導(dǎo)致ReentrantLock的源碼非常簡(jiǎn)單,一塊學(xué)習(xí)一下。

先看一下ReentrantLock怎么使用?

1. ReentrantLock的使用

/**
 * @author 一燈架構(gòu)
 * @apiNote ReentrantLock示例
 **/
public class ReentrantLockDemo {
    
    public static void main(String[] args) {
        // 1. 創(chuàng)建ReentrantLock對(duì)象
        ReentrantLock lock = new ReentrantLock();
        // 2. 加鎖
        lock.lock();
        try {
            // 3. 這里執(zhí)行具體的業(yè)務(wù)邏輯
        } finally {
            // 4. 釋放鎖
            lock.unlock();
        }
    }
}

可以看到ReentrantLock的使用非常簡(jiǎn)單,調(diào)用lock加鎖,unlock釋放鎖,需要配置try/finally使用,保證在代碼執(zhí)行出錯(cuò)的時(shí)候也能釋放鎖。

ReentrantLock也可以配合Condition條件使用,具體可以翻一下前幾篇文章中BlockingQueue的源碼解析,那里面有ReentrantLock的實(shí)際使用。

再看一下ReentrantLock的類結(jié)構(gòu)

2. ReentrantLock類結(jié)構(gòu)

// 實(shí)現(xiàn)Lock接口
public class ReentrantLock implements Lock {

    // 只有一個(gè)Sync同步變量
    private final Sync sync;

    // Sync繼承自AQS,主要邏輯都在這里面
    abstract static class Sync extends AbstractQueuedSynchronizer {
    }

    // Sync的兩個(gè)子類,分別實(shí)現(xiàn)了公平鎖和非公平鎖
    static final class FairSync extends Sync {
    }
    static final class NonfairSync extends Sync {
    }

}

可以看出ReentrantLock的類結(jié)構(gòu)非常簡(jiǎn)單,實(shí)現(xiàn)了Lock接口。

類里面有兩個(gè)靜態(tài)內(nèi)部類,分別實(shí)現(xiàn)公平鎖和非公平鎖。

看一下Lock接口中,定義了哪些方法?

public interface Lock {

    // 加鎖
    void lock();

    // 加可中斷的鎖
    void lockInterruptibly() throws InterruptedException;

    // 嘗試加鎖
    boolean tryLock();

    // 一段時(shí)間內(nèi),嘗試加鎖
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    // 釋放鎖
    void unlock();

    // 新建條件狀態(tài)
    Condition newCondition();
}

就是一些使用鎖的常用方法。

在上篇文章中瀏覽AQS源碼的時(shí)候,了解到AQS定義了一些有關(guān)具體加鎖、釋放鎖的抽象方法,留給子類去實(shí)現(xiàn),再看一下有哪些抽象方法:

// 加獨(dú)占鎖
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
// 釋放獨(dú)占鎖
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}

// 加共享鎖
protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}
// 釋放共享鎖
protected boolean tryReleaseShared(int arg) {
    throw new UnsupportedOperationException();
}

// 判斷是否是當(dāng)前線程正在持有鎖
protected boolean isHeldExclusively() {
    throw new UnsupportedOperationException();
}

由于ReentrantLock使用的是獨(dú)占鎖,所以只需要實(shí)現(xiàn)獨(dú)占鎖相關(guān)的方法就可以了。

3. ReentrantLock源碼解析

3.1 ReentrantLock構(gòu)造方法

// 默認(rèn)的構(gòu)造方法,使用非公平鎖
public ReentrantLock() {
    sync = new NonfairSync();
}

// 傳true,可以指定使用公平鎖
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

在創(chuàng)建ReentrantLock對(duì)象的時(shí)候,可以指定使用公平鎖還是非公平鎖,默認(rèn)使用非公平鎖,顯然非公平鎖的性能更好。

先思考一個(gè)面試??紗?wèn)題,公平鎖和非公平鎖是怎么實(shí)現(xiàn)的?

3.2 非公平鎖源碼

先看一下加鎖源碼:

從父類ReentrantLock的加鎖方法入口:

public class ReentrantLock implements Lock {
    // 加鎖入口方法
    public void lock() {
        // 調(diào)用Sync中加鎖方法
        sync.lock();
    }
}

在子類NonfairSync的加鎖方法:

// 非公平鎖
static final class NonfairSync extends Sync {

    // 加鎖
    final void lock() {
        // 1. 先嘗試加鎖(使用CAS設(shè)置state=1)
        if (compareAndSetState(0, 1))
            // 2. 加鎖成功,就把當(dāng)前線程設(shè)置為持有鎖線程
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // 3. 沒(méi)加鎖成功,再調(diào)用父類AQS中實(shí)際的加鎖邏輯
            acquire(1);
    }
}

加鎖邏輯也很簡(jiǎn)單,先嘗試使用CAS加鎖(也就是把state從0設(shè)置成1),加鎖成功,就把當(dāng)前線程設(shè)置為持有鎖線程。

設(shè)計(jì)者很聰明,在鎖競(jìng)爭(zhēng)不激烈的情況下,很大概率可以加鎖成功,也就不用走else中復(fù)雜的加鎖邏輯了。

如果沒(méi)有加鎖成功,還是需要走else中調(diào)用父類AQS的acquire方法,而acquire又需要調(diào)用子類的tryAcquire方法。

調(diào)用鏈路就是下面這樣:

根據(jù)調(diào)用鏈路,實(shí)際的加鎖邏輯在Sync.nonfairTryAcquire方法里面。

abstract static class Sync extends AbstractQueuedSynchronizer {
    // 非公平鎖的最終加鎖方法
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 1. 獲取同步狀態(tài)
        int c = getState();
        // 2. state=0表示無(wú)鎖,先嘗試加鎖(使用CAS設(shè)置state=1)
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                // 3. 加鎖成功,就把當(dāng)前線程設(shè)置為持有鎖線程
                setExclusiveOwnerThread(current);
                return true;
            }
            // 4. 如果當(dāng)前線程已經(jīng)持有鎖,執(zhí)行可重入的邏輯
        } else if (current == getExclusiveOwnerThread()) {
            // 5. 加鎖次數(shù)+acquires
            int nextc = c + acquires;
            // 6. 超過(guò)tnt類型最大值,溢出了
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

再看一下釋放鎖的調(diào)用流程,公平鎖和非公平鎖流程是一樣的,最終都是執(zhí)行Sync.tryRelease方法:

abstract static class Sync extends AbstractQueuedSynchronizer {
    // 釋放鎖
    protected final boolean tryRelease(int releases) {
        // 1. 同步狀態(tài)減去釋放鎖次數(shù)
        int c = getState() - releases;
        // 2. 校驗(yàn)當(dāng)前線程不持有鎖,就報(bào)錯(cuò)
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        // 3. 判斷同步狀態(tài)是否等于0,無(wú)鎖后,就刪除持有鎖的線程
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}

再看一下公平鎖的源碼

3.3 公平鎖源碼

先看一下公平鎖的加鎖流程:

最終的加鎖方法是FairSync.tryAcquire,看一下具體邏輯:

static final class FairSync extends Sync {

    // 實(shí)現(xiàn)父類的加鎖邏輯
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 1. 獲取同步狀態(tài)
        int c = getState();
        // 2. state=0表示無(wú)鎖,先嘗試加鎖(使用CAS設(shè)置state=1)
        if (c == 0) {
            // 3. 判斷當(dāng)前線程是不是頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)(講究先來(lái)后到)
            if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
            // 4. 如果當(dāng)前線程已經(jīng)持有鎖,執(zhí)行可重入的邏輯
        } else if (current == getExclusiveOwnerThread()) {
            // 5. 加鎖次數(shù)+acquires
            int nextc = c + acquires;
            // 6. 超過(guò)tnt類型最大值,溢出了
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }

    // 判斷當(dāng)前線程是不是頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)(講究先來(lái)后到)
    public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
    }
}

公平鎖的釋放鎖邏輯跟非公平鎖一樣,上面已經(jīng)講過(guò)。

4. 總結(jié)

看完了ReentrantLock的所有源碼,是不是覺(jué)得ReentrantLock很簡(jiǎn)單。

由于加鎖流程的編排工作已經(jīng)在父類AQS中實(shí)現(xiàn),子類只需要實(shí)現(xiàn)具體的加鎖邏輯即可。

加鎖邏輯也很簡(jiǎn)單,也就是修改同步狀態(tài)state的值和持有鎖的線程exclusiveOwnerThread。

到此這篇關(guān)于深入剖析Java ReentrantLock的源碼的文章就介紹到這了,更多相關(guān)Java ReentrantLock內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 23種設(shè)計(jì)模式(19)java責(zé)任鏈模式

    23種設(shè)計(jì)模式(19)java責(zé)任鏈模式

    這篇文章主要為大家詳細(xì)介紹了23種設(shè)計(jì)模式之java責(zé)任鏈模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Java 常見(jiàn)的限流算法詳細(xì)分析并實(shí)現(xiàn)

    Java 常見(jiàn)的限流算法詳細(xì)分析并實(shí)現(xiàn)

    大數(shù)據(jù)量高并發(fā)訪問(wèn)時(shí),經(jīng)常出現(xiàn)服務(wù)或接口面對(duì)暴漲的請(qǐng)求而不可用的情況,甚至引發(fā)連鎖反映導(dǎo)致整個(gè)系統(tǒng)崩潰。此時(shí)你需要使用的技術(shù)手段之一就是限流,當(dāng)請(qǐng)求達(dá)到一定的并發(fā)數(shù)或速率,就進(jìn)行等待、排隊(duì)、降級(jí)、拒絕服務(wù)等。限流時(shí),常見(jiàn)算法是計(jì)數(shù)器、漏斗、令牌桶算法
    2022-04-04
  • 在Java8中構(gòu)建Stream流的多種方式詳解

    在Java8中構(gòu)建Stream流的多種方式詳解

    當(dāng)我們處理集合數(shù)據(jù)時(shí),往往需要對(duì)其進(jìn)行各種操作,如過(guò)濾、映射、排序、歸約等,在 Java 8 中引入的 Stream 流為我們提供了一種更加簡(jiǎn)潔和靈活的方式來(lái)處理數(shù)據(jù),本文將介紹如何基于 Stream 構(gòu)建流,為你展示創(chuàng)建和操作流的多種方法
    2023-08-08
  • Java調(diào)用第三方接口示范的實(shí)現(xiàn)

    Java調(diào)用第三方接口示范的實(shí)現(xiàn)

    這篇文章主要介紹了Java調(diào)用第三方接口示范的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Spring使用@Filter注解創(chuàng)建自定義過(guò)濾器

    Spring使用@Filter注解創(chuàng)建自定義過(guò)濾器

    Spring 中鮮為人知但非常有用的注解之一是 @Filter,它支持自定義過(guò)濾器,下面我們就來(lái)深入研究一下如何使用 Spring 的 @Filter 注解來(lái)創(chuàng)建自定義過(guò)濾器吧
    2023-11-11
  • SpringBoot Controller Post接口單元測(cè)試示例

    SpringBoot Controller Post接口單元測(cè)試示例

    今天小編就為大家分享一篇關(guān)于SpringBoot Controller Post接口單元測(cè)試示例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • java 用泛型參數(shù)類型構(gòu)造數(shù)組詳解及實(shí)例

    java 用泛型參數(shù)類型構(gòu)造數(shù)組詳解及實(shí)例

    這篇文章主要介紹了java 用泛型參數(shù)類型構(gòu)造數(shù)組詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • 將idea中xml文件背景顏色去除的圖文教程

    將idea中xml文件背景顏色去除的圖文教程

    這篇文章主要介紹了將idea中xml文件背景顏色去除,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • java正則表達(dá)式獲取指定HTML標(biāo)簽的指定屬性值且替換的方法

    java正則表達(dá)式獲取指定HTML標(biāo)簽的指定屬性值且替換的方法

    下面小編就為大家?guī)?lái)一篇java正則表達(dá)式獲取指定HTML標(biāo)簽的指定屬性值且替換的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-12-12
  • springboot配置多個(gè)數(shù)據(jù)源兩種方式實(shí)現(xiàn)

    springboot配置多個(gè)數(shù)據(jù)源兩種方式實(shí)現(xiàn)

    在我們的實(shí)際業(yè)務(wù)中可能會(huì)遇到;在一個(gè)項(xiàng)目里面讀取多個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù)來(lái)進(jìn)行展示,spring對(duì)同時(shí)配置多個(gè)數(shù)據(jù)源是支持的,本文主要介紹了springboot配置多個(gè)數(shù)據(jù)源兩種方式實(shí)現(xiàn),感興趣的可以了解一下
    2022-03-03

最新評(píng)論