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

ReentrantrantLock底層實現(xiàn)原理及分析

 更新時間:2026年03月14日 09:32:35   作者:快樂的小三菊  
本文詳細介紹了Java并發(fā)包中ReentrantLock的實現(xiàn)原理,包括非公平鎖和公平鎖的加鎖和解鎖機制,以及可重入和可打斷特性,同時,還介紹了條件變量的實現(xiàn)原理,通過await和signal方法來管理線程間的等待和喚醒

一、類圖

ReentrantrantLock 的類圖如下所示,可以看到,它也是實現(xiàn)了 Lock 接口,和我們自定義不可重入鎖的步驟是一樣的,其內(nèi)部也是維護了一個內(nèi)部類同步器類 Sync,這個同步器類也是繼承自 AQS,不過需要注意的是這里面的同步器類 Sync 是抽象的,有兩個實現(xiàn),一個是非公平鎖實現(xiàn) NonfairSync,一個是公平鎖實現(xiàn) FairSync。

二、非公平鎖實現(xiàn)原理

2.1 加鎖流程

先從構造器開始看,默認為非公平鎖實現(xiàn),如下代碼,其中 NonfairSync 繼承自 AQS

    public ReentrantLock() {
        sync = new NonfairSync();
    }

先看加鎖流程,如下的 lock() 方法,可以看到,方法內(nèi)部調(diào)用的是NonfairSync 實現(xiàn)的 lock() 方法

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

接下來我們看下NonfairSync lock() 方法是如何實現(xiàn)的,如下圖:

當沒有競爭的時候,通過 CAS state 0 改成 1,并把 owner 線程改成自己,如下圖:

當?shù)谝粋€競爭出現(xiàn)時,如下圖,此時owner 已經(jīng)是 Thread0 了,此時來了一個 Thread-1,此時 Thread-1 還是執(zhí)行 lock() 方法。

此時 Thread-1 CAS 肯定是失敗的,此時就進入 acquire() 方法,此方法的內(nèi)容如下所示:

1、進入 tryAcquire 邏輯,這時 state 已經(jīng)是 1,結果仍然失敗。

2、接下來進入 addWaiter 邏輯,構造 Node 隊列,如下圖,會在 head 后面添加一個節(jié)點,這個節(jié)點是雙向鏈表,即 head 指向一個 node 節(jié)點,node 節(jié)點又指向下一個 node 節(jié)點。

圖中黃色三角表示該 Node waitStatus 狀態(tài),其中 0 為默認正常狀態(tài),Node 的創(chuàng)建是懶惰的。

需要注意的是,首次創(chuàng)建 node 時會創(chuàng)建兩個,其中第一個 Node 稱為 Dummy(啞元)或哨兵,用來占位,并不關聯(lián)線程。只有第二個 Node 才會關聯(lián)線程 Thread-1,因為它競爭失敗了,因為它是第一個加入的,所以它指向了鏈表的尾部。其實這個就是我們的等待隊列。

等到創(chuàng)建成功之后,當前線程就會進入到acquireQueued 邏輯,如下圖,這個方法主要是在一個死循環(huán)中不斷嘗試獲得鎖,失敗后進入 park 阻塞。

1、首先獲取前驅節(jié)點,然后檢查前驅節(jié)點,判斷前驅階段是否為頭節(jié)點,如果是,說明 Thread-1 的這個 Node 是處于第二位的,那么它就有資格再次調(diào)用 tryAcquire() 方法再次獲取鎖。

2、如果還是沒有獲取到鎖,此時就會進入到shouldParkAfterFailedAcquire 邏輯,這個方法會把當前線程節(jié)點的前驅節(jié)點的 waitStatus 改為 -1,改為 -1 的意思是它有責任喚醒它的后繼節(jié)點,此時方法返回 false,如下圖:


3、shouldParkAfterFailedAcquire 執(zhí)行完畢回到 acquireQueued ,再次 tryAcquire 嘗試獲取鎖,當然這時state 仍為 1,失敗。

4、當再次進入 shouldParkAfterFailedAcquire 時,這時因為其前驅 node waitStatus 已經(jīng)是 -1,這次返回 true。

5、此時就會進入 parkAndCheckInterrupt(),即 Thread-1 被阻塞住,用灰顏色表示,如下圖:

再次有多個線程經(jīng)歷上述過程競爭失敗,就會變成如下的樣子:

2.2 解鎖流程

解鎖流程,如下的 lock() 方法,可以看到,方法內(nèi)部調(diào)用的同步器 AQS release() 方法。

    public void unlock() {
        sync.release(1);
    }

release()方法的內(nèi)容如下所示:

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

假設此時 Thread-0 釋放鎖,就會進入到 tryRelease() 方法,如果返回 true,就證明釋放成功了,就會把owner 線程設置為 null,并且把 state 設置為 0,如下圖:

tryRelease() 方法返回 true 之后,還需要檢查隊列中的 head 是否為空,還需要檢查 head 中的 waitState 是否不等于 0 ,若返回 true ,則需要調(diào)用 unparkSuccessor() 方法喚醒下一個節(jié)點。

unparkSuccessor() 方法是找到隊列中離 head 最近的一個 Node(沒取消的),unpark 恢復其運行,本例中即為 Thread-1,此時回到 Thread-1 acquireQueued() 方法的流程,如下圖:

如果加鎖成功(沒有競爭),會設置 exclusiveOwnerThread Thread-1,state = 1;head 指向剛剛 Thread-1 所在的 Node,該 Node 清空 Thread;原本的 head 因為從鏈表斷開,而可被垃圾回收。

如果這時候有其它線程來競爭(非公平的體現(xiàn)),例如這時有 Thread-4 來了,Thread-4 并不是阻塞隊列里面的線程,如下圖:

如果不巧又被 Thread-4 占了先,那么 Thread-4 被設置為 exclusiveOwnerThread,state = 1;Thread-1 再次進入 acquireQueued 流程,獲取鎖失敗,重新進入 park 阻塞。

三、可重入原理

接下來我們看下非公平鎖的可重入的實現(xiàn)原理,我們看下他的獲取鎖和釋放鎖的相關代碼,

獲取鎖最終調(diào)用的是 nonfairTryAcquire() 方法,傳入的 acquires 就是 1,首先判斷 state 的狀態(tài)是否是 0,如果是 0 就代表還沒有別人獲取鎖,然后我就去 CAS ,如果成功則設置 owner 設置為自己,然后返回 true。

如果是同一個線程下次又調(diào)用nonfairTryAcquire() 方法,此時就回去判斷當前線程是否為 owner 持有的線程,如果是,則證明發(fā)生了鎖重入,此時就是將 state 的值 ++,由 1 變成了 2,

static final class NonfairSync extends Sync {
    // ...

    // Sync 繼承過來的方法, 方便閱讀, 放在此處
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 如果已經(jīng)獲得了鎖, 線程還是當前線程, 表示發(fā)生了鎖重入
        else if (current == getExclusiveOwnerThread()) {
            // state++
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

鎖釋放的時候調(diào)用的是 tryRelease() 方法,如下所示,此時就會把 state 的值 --,此時返回一個 false,表示我只是讓 state -- ,并沒有真正的釋放鎖,直到下面的這個 c 變成 0 了,才會釋放鎖,返回 true

    // Sync 繼承過來的方法, 方便閱讀, 放在此處
    protected final boolean tryRelease(int releases) {
        // state--
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        // 支持鎖重入, 只有 state 減為 0, 才釋放成功
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

四、可打斷原理

4.1 不可打斷模式

在次模式下,即使它被打斷,仍會駐留在 AQS 隊列中,一直要等到獲得鎖后方能得知自己被打斷了。

我們都知道線程在知道自己沒有辦法獲取鎖的時候就會進入到 acquireQueued() 方法的循環(huán)內(nèi)不斷的去嘗試,如果還不成功,就會進入到parkAndCheckInterrupt() 方法里面等待。

但是進入到 parkAndCheckInterrupt() 方法的線程可以被其他的線程調(diào)用它的 interrupted() 方法喚醒,如下面的parkAndCheckInterrupt() 方法,會返回一個 boolean 表示是否被打斷過,但是interrupted() 方法還會清除打斷標記,即下次再 park 還可以 park 住。

如果parkAndCheckInterrupt() 返回 true 了就會進入 if 塊了,此時就會對打斷標記做一個記錄,設置值為 true,沒有做額外的處理,接下來還是會繼續(xù) for 循環(huán),獲取不了鎖還是會 park 阻塞。只有當你獲取到鎖以后,會把打斷標記作為結果返回。

此時就會進入到selfInterrupt() 方法,重新產(chǎn)生一次中斷。

static final class NonfairSync extends Sync {
    // ...

    private final boolean parkAndCheckInterrupt() {
        // 如果打斷標記已經(jīng)是 true, 則 park 會失效
        LockSupport.park(this);
        // interrupted 會清除打斷標記
        return Thread.interrupted();
    }

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (; ; ) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null;
                    failed = false;
                    // 還是需要獲得鎖后, 才能返回打斷狀態(tài)
                    return interrupted;
                }
                if (
                        shouldParkAfterFailedAcquire(p, node) &&
                                parkAndCheckInterrupt()
                ) {
                    // 如果是因為 interrupt 被喚醒, 返回打斷狀態(tài)為 true
                    interrupted = true;
                }
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

    public final void acquire(int arg) {
        if (
                !tryAcquire(arg) &&
                        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
        ) {
            // 如果打斷狀態(tài)為 true
            selfInterrupt();
        }
    }

    static void selfInterrupt() {
        // 重新產(chǎn)生一次中斷
        Thread.currentThread().interrupt();
    }
}

4.2 可打斷模式

可打斷模式就是在調(diào)用acquireInterruptibly() 方法獲取鎖時可以被打斷,它和上面的方法區(qū)別是當線程被喚醒的時候,即調(diào)用doAcquireInterruptibly() 時會拋出一個異常,打斷 for 循環(huán)等待,即線程停止去等待鎖

// Sync 繼承自 AQS
static final class NonfairSync extends Sync {
    public final void acquireInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 如果沒有獲得到鎖, 進入 ㈠
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    // ㈠ 可打斷的獲取鎖流程
    private void doAcquireInterruptibly(int arg) throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt()) {
                    // 在 park 過程中如果被 interrupt 會進入此
                    // 這時候拋出異常, 而不會再次進入 for (;;)
                    throw new InterruptedException();
                }
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
}

五、公平鎖實現(xiàn)原理

公平鎖和非公平鎖的區(qū)別主要就是在 tryAcquire() 方法的實現(xiàn)中,當 c==0 即還沒有別人占這個鎖時,先調(diào)用 hasQueuedPredecessors() 方法判斷隊列中是否有前驅節(jié)點,沒有才回去競爭,即只能是老二節(jié)點線程才可以運行,非老二節(jié)點線程不能運行。

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }

    // AQS 繼承過來的方法, 方便閱讀, 放在此處
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
                        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
        ) {
            selfInterrupt();
        }
    }

    // 與非公平鎖主要區(qū)別在于 tryAcquire 方法的實現(xiàn)
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 先檢查 AQS 隊列中是否有前驅節(jié)點, 沒有才去競爭
            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;
    }

    // ㈠ AQS 繼承過來的方法, 方便閱讀, 放在此處
    public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        // h != t 時表示隊列中有 Node
        return h != t &&
                (
                        // (s = h.next) == null 表示隊列中還有沒有老二
                        (s = h.next) == null ||
                                // 或者隊列中老二線程不是此線程
                                s.thread != Thread.currentThread()
                );
    }
}

六、條件變量實現(xiàn)原理

每個條件變量其實就對應著一個等待隊列,其實現(xiàn)類就是 ConditionObject。

6.1 await 流程

假設一開始 Thread-0 持有鎖,如下圖所示:

接下來如果線程調(diào)用了ConditionObject 類的 await() 方法,它就會進入到ConditionObject addConditionWaiter 流程,如下圖:

addConditionWaiter () 方法會把調(diào)用的線程加入到條件變量里面的雙向鏈表之中,并且把新的 Node 狀態(tài)變?yōu)?strong> -2,表示在條件變量中處于等待狀態(tài),如下圖:

接下來會進入到AQS fullyRelease 流程,釋放同步器上的鎖,如下圖:

接下來會 unpark AQS 隊列中的下一個節(jié)點,競爭鎖,假設沒有其他競爭線程,那么 Thread-1 競爭成功,如下圖所示:

此時 Thread-0 的狀態(tài)就被 park 阻塞了,如下圖:

6.2 signal 流程

假設此時 Thread-1 要來喚醒 Thread-0,如下圖:

signal() 方法的代碼如下所示,首先進行健康檢查,看調(diào)用方法的線程是否為鎖的持有者,即只有 owner 才可以喚醒其他線程,接下來查找條件變量列表中的第一個元素,查找完畢之后調(diào)用 doSignal() 方法。

doSignal() 方法是取得等待隊列中第一個 Node,即 Thread-0 所在 Node

此時的狀態(tài)圖如下所示:

獲取完Node 之后,調(diào)用 transferForSignal() 方法,將該 Node 加入 AQS 隊列尾部,將 Thread-0 waitStatus 改為 0Thread-3 waitStatus 改為 -1

接下來就是 Thread-1 釋放鎖,把 state 置為 0,設置 owner null,喚醒等待隊列中的下一個元素了,整個流程結束。

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • java實現(xiàn)斗地主發(fā)牌功能

    java實現(xiàn)斗地主發(fā)牌功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)斗地主發(fā)牌功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • Java的垃圾回收機制實例分析

    Java的垃圾回收機制實例分析

    這篇文章主要介紹了Java的垃圾回收機制,結合實例形式分析了垃圾回收機制的原理及相關操作技巧,需要的朋友可以參考下
    2019-08-08
  • Spring監(jiān)聽器及定時任務實現(xiàn)方法詳解

    Spring監(jiān)聽器及定時任務實現(xiàn)方法詳解

    這篇文章主要介紹了Spring監(jiān)聽器及定時任務實現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • Java中finalize()詳解及用法

    Java中finalize()詳解及用法

    這篇文章主要介紹了Java中finalize()詳解及用法的相關資料,final是Java的關鍵字,它所表示的是“這部分是無法修改的”,需要的朋友可以參考下
    2017-03-03
  • 深入理解Java Optional使用實踐記錄

    深入理解Java Optional使用實踐記錄

    Java8引入Optional類,用于優(yōu)雅處理可能為空的值,避免空指針異常,提供創(chuàng)建、轉換、過濾方法,支持鏈式調(diào)用,但應避免作為字段類型或集合元素,以防性能損耗,本文給大家介紹Java Optional使用實踐記錄,感興趣的朋友一起看看吧
    2025-06-06
  • java實現(xiàn)微信支付功能

    java實現(xiàn)微信支付功能

    這篇文章主要為大家詳細介紹了java實現(xiàn)微信支付功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • Rxjava+Retrofit+MVP實現(xiàn)購物車功能

    Rxjava+Retrofit+MVP實現(xiàn)購物車功能

    這篇文章主要為大家詳細介紹了Rxjava+Retrofit+MVP實現(xiàn)購物車功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Springboot實現(xiàn)獲取實時天氣

    Springboot實現(xiàn)獲取實時天氣

    這篇文章主要為大家詳細介紹了如何使用Springboot實現(xiàn)獲取實時天氣功能,文中的示例代碼講解詳細,有需要的小伙伴可以跟隨小編一起學習一下
    2024-04-04
  • Java JDBC數(shù)據(jù)庫連接失敗的7種常見原因及解決方案

    Java JDBC數(shù)據(jù)庫連接失敗的7種常見原因及解決方案

    Java JDBC(Java Database Connectivity)是Java平臺中用于執(zhí)行SQL語句的標準API,它為開發(fā)者提供了與各種關系型數(shù)據(jù)庫進行交互的能力,本文為大家介紹了Java JDBC數(shù)據(jù)庫連接失敗的7種常見原因及解決方案,需要的朋友可以參考下
    2025-11-11
  • 詳解Java的TCP/IP編程學習--基于定界符的成幀

    詳解Java的TCP/IP編程學習--基于定界符的成幀

    這篇文章主要介紹了Java的TCP/IP編程學習--基于定界符的成幀,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04

最新評論

会同县| 肥东县| 庆安县| 深泽县| 崇左市| 榕江县| 澎湖县| 屯留县| 鄂温| 周宁县| 阜宁县| 呼伦贝尔市| 广水市| 中宁县| 永寿县| 柳江县| 绍兴市| 千阳县| 宁明县| 洞口县| 石楼县| 湄潭县| 额济纳旗| 沧州市| 九龙坡区| 明溪县| 闵行区| 民乐县| 磴口县| 夹江县| 长阳| 武城县| 六盘水市| 内丘县| 龙岩市| 丹巴县| 和林格尔县| 鄂托克旗| 崇明县| 贵州省| 汕头市|