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

ReentrantLock 非公平鎖實現(xiàn)原理詳解

 更新時間:2022年12月14日 10:44:01   作者:小海編碼日記  
這篇文章主要為大家介紹了ReentrantLock 非公平鎖實現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

在線程間通信方式2一節(jié)中,我們了解了Lock,Condition和ReentrantLock,學習了簡單使用Condition和RentrantLock完成線程間通信,從文章中我們了解到ReentrantLock是Lock接口的一個最常用的實現(xiàn)類,ReentrantLock是獨占鎖,獨占鎖的場景下又支持公平鎖和非公平鎖,那么在源碼實現(xiàn)中,ReentrantLock繼承關系,實現(xiàn)結構又是怎樣的呢?

ReentrantLock繼承關系及關聯(lián)類

在多線程與鎖中,我們了解到ReentrantLock是支持公平鎖和非公平鎖的,對應的構造函數(shù)源碼如下:

 // ReentrantLock.java
 public ReentrantLock(boolean fair) {
     sync = fair ? new FairSync() : new NonfairSync();
 }

可以看到如果是公平鎖則創(chuàng)建FairSync對象,如果是非公平鎖則創(chuàng)建NonfairSync,再結合Lock接口核心的lock,tryLock,unlock函數(shù)源碼(Lock接口在線程間通信方式2中有介紹)可以看出,在ReentrantLock中,使用FairSync或NonfairSync代理了鎖狀態(tài)管理,lock,tryLock和unlock實現(xiàn)源碼如下:

 // ReentrantLock.java
 public void lock() {
     sync.lock();
 }
 ?
 public boolean tryLock() {
     return sync.nonfairTryAcquire(1);
 }
 ?
 public void unlock() {
     sync.release(1);
 }

由此,進一步梳理ReentrantLock實現(xiàn),可以得到下圖:

上圖中一些生僻類及其作用見下表:

類名說明備注
FairSyncReentrantLock中公平鎖的實現(xiàn)類/
NonfairSyncReentrantLock中非公平鎖的實現(xiàn)類/
Sync公平鎖和非公平鎖實現(xiàn)類的共同父類,使用AbstractQueuedSynchronizer狀態(tài)記錄鎖的持有數(shù)據(jù)/
AbstractQueuedSynchronizerAbstractQueuedSynchronizer簡稱AQS,一個用于實現(xiàn)阻塞鎖和同步器的工具類,其內(nèi)部維護一個先進先出的等待隊列(真實數(shù)據(jù)結構是雙向鏈表),依賴一個int型的數(shù)據(jù)管理同步狀態(tài)/
AbstractOwnableSynchronizerAQS的父類,定義了一個線程獨占的同步器,用于實現(xiàn)創(chuàng)建鎖和鎖占有標記的基礎類,其內(nèi)部使用exclusiveOwnerThread記錄當前占用鎖的線程/
Serializable序列化接口/

ReentrantLock.lock流程分析

跟蹤ReentrantLock.lock調(diào)用流程,可以得到下面的時序圖(以非公平鎖為例分析):

上圖中描述了ReentrantLock中l(wèi)ock在資源空和資源被占用的情況下的執(zhí)行流程,接下來我們來看下其內(nèi)部的細節(jié)實現(xiàn)。

ReentrantLock.lock獲取鎖成功

NonfairSynck類中的lock代碼如下所示:

 // NonfairSync.java
 final void lock() {
     if (compareAndSetState(0, 1))
         setExclusiveOwnerThread(Thread.currentThread());
     else
         acquire(1);
 }

可以看出,我們在調(diào)用了ReentrantLock.lock后,NonfairSync會首先嘗試通過CAS將資源占用狀態(tài)置為1(compareAndSetState,默認值0,期望值1),如果執(zhí)行成功,說明當前獲取共享資源成功,將當前線程設置為獨占鎖持有者,當前線程繼續(xù)執(zhí)行。

compareAndSetState

compareAndSetState操作的是AQS中聲明的一個int型的值,如果其值為0,表示當前鎖空閑,如果有線程到來可以占用鎖,如果值大于1,表示當前鎖被占用,為保證多線程對該值的操作實時可見,使用volatile修飾該變量,相關代碼如下:

 // AbstractQueuedSynchronizer.java
 private volatile int state;
 protected final boolean compareAndSetState(int expect, int update) {
     // See below for intrinsics setup to support this
     return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
 }

setExclusiveOwnerThread

setExclusiveOwnerThread最終是將當前線程設置到AbstractOwnableSynchronizer中定義的exclusiveOwnerThread中,代碼如下:

 // AbstractOwnableSynchronizer.java
 private transient Thread exclusiveOwnerThread;
 ?
 protected final void setExclusiveOwnerThread(Thread thread) {
     exclusiveOwnerThread = thread;
 }

transient關鍵字:

transient關鍵字的主要作用是讓某些被transient關鍵字修飾的變量不被序列化,如果對transient修飾的變量執(zhí)行了序列化,則該變量會重新執(zhí)行默認初始化,反序列化得到的對象是null

ReentrantLock.lock獲取鎖失敗

前文中可以看到如果compareAndSetState執(zhí)行返回false的話,就說明當前共享資源被占用,隨后走else邏輯,執(zhí)行acquire(1),其內(nèi)容如下:

 // NonfairSync.java
 final void lock() {
     if (compareAndSetState(0, 1))
         setExclusiveOwnerThread(Thread.currentThread());
     else
         acquire(1);
 }
 ?
 // AbstractQueuedSynchronizer.java
 public final void acquire(int arg) {
     if (!tryAcquire(arg) &&
         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
         selfInterrupt();
 }

其內(nèi)部主要由兩塊組成,tryAcquire和acquireQueued,其中tryAcquire再次嘗試獲取鎖,如果獲取成功,則流程結束,當前線程正常繼續(xù)執(zhí)行,如果獲取失敗,則執(zhí)行acquireQueued方法(由于使用且操作符連接tryAcquire和acquireQueued,所以只有tryAcquire返回false的時候,才會執(zhí)行acquireQueued方法),該方法接受addWaiter返回的Node參數(shù),下面來詳細看下兩個函數(shù)的具體實現(xiàn)。

tryAcquire

tryAcquire在NonfairSync中的實現(xiàn)如下,其最終調(diào)用到的是Sync類的nonfairTryAcquire方法:

 // NonfairSync.java
 protected final boolean tryAcquire(int acquires) {
     return nonfairTryAcquire(acquires);
 }
 // Sync.java
 final boolean nonfairTryAcquire(int acquires) {
     final Thread current = Thread.currentThread();
     // 獲取當前同步資源狀態(tài)
     int c = getState();
     // 同步資源空閑
     if (c == 0) {
         // 占用同步資源更新資源狀態(tài)
         if (compareAndSetState(0, acquires)) {
             // 設置當前線程為資源對應的獨占鎖持有者
             setExclusiveOwnerThread(current);
             return true;
         }
     }
     // 如果當前線程就是持有鎖線程,更新鎖狀態(tài),直接持有鎖
     else if (current == getExclusiveOwnerThread()) {
         int nextc = c + acquires;
         if (nextc < 0) // overflow
             throw new Error("Maximum lock count exceeded");
         setState(nextc);
         return true;
     }
     return false;
 }

可以看到在tryAcquire中實際上也是嘗試獲取鎖的過程,首先檢查當前當前同步資源狀態(tài),如果不可獲取,則檢查當前線程時否是持鎖線程,是的話則直接獲取鎖(可重入鎖的實現(xiàn)),更新同步資源狀態(tài),如果均失敗,則返回false。

acquireQueued

acquireQueued在AQS中關聯(lián)的核心代碼如下所示,可以看出主要包含addWaiter和acquireQueued兩部分:

addWaiter

// AbstractQueuedSynchronizer.java
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
// AbstractQueuedSynchronizer.java
private Node addWaiter(Node mode) {
    // 創(chuàng)建新的Node對象
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

可以看到不管是addWaiter還是enq,其最終目標都是基于當前線程創(chuàng)建新的Node對象,將新的Node對象添加在隊尾,前文提到AQS中維護了一個先進先出的隊列,其數(shù)據(jù)結構本質(zhì)是雙向鏈表,這里的head,tail就是鏈表的具體實現(xiàn)。Node類中包含了指向前一個元素和后一個元素的引用,Node的聲明如下:

// Node實體類
static final class Node {
    ...
    // 前一個元素
    volatile Node prev;
    // 下一個元素
    volatile Node next;
    // 線程對象
    volatile Thread thread;
    Node() {    // Used to establish initial head or SHARED marker
    }
    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }
    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
    ...
}
// AbstractQueuedSynchronizer.java中聲明Node對象
// 鏈表頭
private transient volatile Node head;
// 鏈表尾部
private transient volatile Node tail;

acquireQueued

acquireQueued源碼如下:

// AbstractQueuedSynchronizer.java
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; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

可以看到在addWaiter中成功將新的Node添加到隊尾后,acquireQueued中當前線程會自旋,嘗試獲取鎖,如果獲取失敗,則執(zhí)行shouldParkAfterFailedAcquire和parkAndCheckInterrupt,這兩個函數(shù)都返回true則執(zhí)行selfInterrupt方法(代碼見acquire函數(shù)部分)。

shouldParkAfterFailedAcquire用于判斷是否應該對當前線程阻塞,如果是的話則返回true,parkAndCheckInterrupt用于執(zhí)行線程阻塞并且判斷當前線程是否處于interrupt狀態(tài),如果是則會返回true。通過源碼可以看到其是通過LockSupport.park進行線程狀態(tài)切換的,代碼如下:

// AbstractQueuedSynchronizer.java
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

LockSupport.park作用

LockSupport.park用于在許可證不可用時阻塞禁用當前線程,如果許可證可用,則該許可證被消耗,并且當前調(diào)用立即返回,否則,出于線程調(diào)度的目的,當前線程將被阻塞禁用,并進入休眠狀態(tài),直到發(fā)生以下三種情況之一:

  • 其他線程以當前線程為參數(shù)調(diào)用unpark
  • 其他線程中斷當前線程
  • 調(diào)用發(fā)生異常

結合上文,我們可以得出ReentrantLock.lock執(zhí)行的一般流程如下所示:

ReentrantLock.unlock流程分析

跟蹤ReentrantLock.unlock調(diào)用流程,可以得到下面的時序圖(以非公平鎖為例分析):

可以看到對于ReentrantLock.unlock流程而言,其核心實現(xiàn)函數(shù)是tryRelease和unparkSuccessor,release函數(shù)如下所示:

// ReentrantLock.java
public void unlock() {
    sync.release(1);
}
// AQS.java
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

從代碼中可以看到當tryRelease執(zhí)行完成返回true后,我們會獲取鏈表首位元素,當首位元素不為空且等待狀態(tài)(waitStatus)不等于0時,執(zhí)行unparkSuccessor,在這里我們又遇到了Node類的另一個核心元素waitStatus,其聲明如下:

static final class Node {
    ...
    // waitStatus的四種可能取值,表示當前節(jié)點及其后續(xù)節(jié)點對應線程的運行狀態(tài)
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    // 聲明waitStatus
    volatile int waitStatus;
    ...
}

四種取值含義如下所示:

  • SIGNAL:取值為-1,該節(jié)點的后續(xù)節(jié)點處于被阻塞或即將阻塞的狀態(tài),因此當前節(jié)點的線程在釋放鎖或取消時必須解除后繼節(jié)點的阻塞狀態(tài),使后續(xù)節(jié)點的線程得以正常運行
  • CANCELLED:取值為1,當前節(jié)點對應的線程由于超時或中斷而被取消,Node的waitStatus取該值,進入取消狀態(tài)后節(jié)點狀態(tài)不再變化
  • CONDITION:取值為-2,該節(jié)點當前在等待隊列中,節(jié)點對應的線程等待Condition,當其他線程對Condition調(diào)用了signal方法后,該節(jié)點從等待隊列中轉(zhuǎn)入鏈表中,進行同步狀態(tài)的獲取
  • PROPAGATE:取值為-3,在共享鎖實現(xiàn)中使用,當前節(jié)點線程處于可運行狀態(tài)

為了簡化使用,對于waitStatus取值并沒有按照數(shù)字遞增或遞減排列進行取值,如果該節(jié)點取值為非負值,則代表不需要將操作同步到其他節(jié)點,對于普通Node節(jié)點而言,waitStatus字段初始化為0,對于條件節(jié)點,該字段初始化為1,在代碼中使用CAS對該字段進行修改。

下面我們來分別看下這兩個核心函數(shù)的實現(xiàn)

tryRelease

tryRelease主要用于對鎖狀態(tài)標記進行清理,函數(shù)實現(xiàn)如下所示:

protected final boolean tryRelease(int releases) {
    // 獲取AQS的鎖狀態(tài)標記,計算剩余鎖狀態(tài)標記
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 鎖狀態(tài)標記為0,當前沒有其他線程持有鎖,鎖處于空閑狀態(tài),設置鎖持有者為null
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    // 更新鎖狀態(tài)
    setState(c);
    return free;
}

在ReentrantLock獨占鎖實現(xiàn)的場景下,state鎖狀態(tài)標記取值只有兩個,0和1(前文lock過程中也有分析通過CAS改變state狀態(tài)時,一直是從0到1的修改),進而當當前線程執(zhí)行tryRelease后,state鎖狀態(tài)標記取值更新為0,表示當前鎖處于空閑狀態(tài),隨后自然要喚醒Node鏈表中的其他節(jié)點去獲取鎖啦。

unparkSuccessor

unparkSuccessor函數(shù)主要用于更新Node節(jié)點的waitStatus狀態(tài)并按需將Node鏈表中阻塞的線程喚醒執(zhí)行,實現(xiàn)如下所示:

private void unparkSuccessor(Node node) {
    // 獲取頭節(jié)點的waitStatus狀態(tài),將頭節(jié)點的waitStatus狀態(tài)設置為0,head waitStatus恢復初始狀態(tài)
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    // 獲取頭節(jié)點的下一個節(jié)點,如果下一個節(jié)點為null或者狀態(tài)為取消狀態(tài)(waitStatus大于0只有取消狀態(tài)),則從尾節(jié)點開始遍歷,查找未被取消的后續(xù)節(jié)點對象
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    // 對上一步獲取到的節(jié)點對應的線程執(zhí)行unpark喚醒,去搶占鎖
    if (s != null)
        LockSupport.unpark(s.thread);
}

從前面可以看出這里傳入的node是當前的head(鏈表頭),判斷當前頭節(jié)點的waitStatus,如果不是初始值則重置為初始值,隨著查找下一個要被喚醒的節(jié)點,查找到后,喚醒節(jié)點對應的線程,讓線程去嘗試搶占鎖。

結合上文,我們可以得出ReentrantLock.unlock執(zhí)行的一般流程如下所示:

以上就是ReentrantLock 非公平鎖實現(xiàn)原理詳解的詳細內(nèi)容,更多關于ReentrantLock 非公平鎖的資料請關注腳本之家其它相關文章!

相關文章

  • JavaWeb請求轉(zhuǎn)發(fā)和請求包含實現(xiàn)過程解析

    JavaWeb請求轉(zhuǎn)發(fā)和請求包含實現(xiàn)過程解析

    這篇文章主要介紹了JavaWeb請求轉(zhuǎn)發(fā)和請求包含實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • 淺談Java中生產(chǎn)者與消費者問題的演變

    淺談Java中生產(chǎn)者與消費者問題的演變

    這篇文章主要介紹了淺談Java中生產(chǎn)者與消費者問題的演變,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-10-10
  • SpringBoot異步方法捕捉異常詳解

    SpringBoot異步方法捕捉異常詳解

    這篇文章主要為大家詳細介紹了SpringBoot異步方法捕捉異常,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • 解決異常處理問題:getReader()?has?already?been?called?for?this

    解決異常處理問題:getReader()?has?already?been?called?for?this

    這篇文章主要介紹了解決異常處理:getReader()?has?already?been?called?for?this問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • SpringCloudConfig之client端報錯Could?not?resolve?placeholder問題

    SpringCloudConfig之client端報錯Could?not?resolve?placeholder問

    這篇文章主要介紹了SpringCloudConfig之client端報錯Could?not?resolve?placeholder?‘from‘?in?value?“${from}“問題及解決方案,具有很好的參考價值,希望對大家有所幫助
    2022-12-12
  • java8新特性之Optional的深入解析

    java8新特性之Optional的深入解析

    這篇文章主要給大家介紹了關于java8新特性之Optional的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-02-02
  • Java常用對象操作工具代碼實例

    Java常用對象操作工具代碼實例

    這篇文章主要介紹了Java常用對象操作工具代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-12-12
  • Spring Boot應用Docker化的步驟詳解

    Spring Boot應用Docker化的步驟詳解

    這篇文章主要給大家介紹了關于Spring Boot應用Docker化的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2018-04-04
  • 講解Java設計模式編程中的建造者模式與原型模式

    講解Java設計模式編程中的建造者模式與原型模式

    這篇文章主要介紹了Java設計模式編程中的建造者模式與原型模式,設計模式有利于團隊開發(fā)過程中的代碼維護,需要的朋友可以參考下
    2016-02-02
  • 關于java中@Async異步調(diào)用詳細解析附代碼

    關于java中@Async異步調(diào)用詳細解析附代碼

    本文主要介紹了java關于@Async異步調(diào)用詳細解析附代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07

最新評論