日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ReentrantLock 分析

發布時間:2025/3/15 编程问答 12 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ReentrantLock 分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

帶著疑問去分析

  • ReentrantLock是如何實現鎖管理的。
  • ReentrantLock是如何實現重入的。
  • ReentrantLock是如何實現公平鎖與非公平鎖。
  • ReentantLock的公平鎖為什么一般情況下性能都比公平鎖查。
  • ReentrantLock數據結構

    ReentrantLock的底層是借助于AbstractQueuedSynchronizer實現的,所以其數據結構依賴于AbstractQueuedSynchronizer的數據結構。

    AQS的數據結構 如下圖:

    ReentrantLock源碼分析

    類的繼承關系

    public class ReentrantLock implements Lock, java.io.Serializable

    說明:ReentrantLock實現了Lock接口,Lock接口中定義了lock與unlock操作,并且還存在newCondition方法,表示生成一個條件。

    類的內部類

    ReentrantLock總共有三個內部類,并且三個內部類是緊密相關的,下面先看三個類的關系:

    說明:

    • ReentrantLock類內部總共存在Sync、NonfairSync、FairSync三個類,NonfairSync與FairSync類繼承Sync類,Sync類繼承自AQS抽象類。
    • ReentrantLock是通過構造方法實現公平鎖與非公平鎖定義的。
    public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync(); }

    Sync類

    Sync類源碼如下:(只列出主要方法)

    /*** 獲取鎖 由于公平鎖和非公平鎖 獲取鎖的動作 不一樣,所以留著讓子類實現更好。*/ abstract void lock();/*** 非公平模式下嘗試獲取鎖。兩種情況:* 1. 鎖沒被占用:* (1). CAS將當前鎖狀態加1* (2). 將當前線程設置為鎖的獨享者。* 2. 鎖被占用* (1). 如果鎖占用者是當前線程 ,則將線程狀態加+1 ,通過這個線程狀態 就可以知道重入次數*/ final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false; }/**** 第一步:依次減去釋放量(releases變量 這里都是1),* 第二步:如果全部釋放(state),則將鎖占用者置為null,表示釋放鎖。* @param releases* @return*/ protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free; }/*** 判斷當前線程是否是鎖的擁有者* @return*/ protected final boolean isHeldExclusively() {return getExclusiveOwnerThread() == Thread.currentThread(); }/*** 查看當前線程重入次數* @return*/ final int getHoldCount() {return isHeldExclusively() ? getState() : 0; }/*** 當前鎖是否被占用* @return*/ final boolean isLocked() {return getState() != 0; }

    說明:Sync類存在如下方法和作用如下。

    NonfairSync類

    NonfairSync類繼承了Sync類,表示采用非公平策略獲取鎖,其實現了Sync類中抽象的lock方法,源碼如下:

    static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;/*** 1. 英文版 注釋:* Performs lock. Try immediate barge, backing up to normal* acquire on failure.** 我的謬論:* barge 在英文中是: 蠻不講理地闖入或打擾某事物 、闖入之意。( 用在此場景,個人感覺很好呀 ^_^ )* 加鎖過程:當一個線程調用lock方法時,直接嘗試加鎖,如果加鎖失敗,再進行正常的獲取。* 從這里可以看出 作者認為 ReentrantLock更多的時候,鎖的爭搶并不激烈,大多時候第一次的barge就能獲取鎖,所以作者才會首先進行barge,失敗再嘗試正常獲取。*/final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}/*** lock 中的 acquire(1) 代碼段可能會調用此方法 , 此方法的作用主要是為了 獲取獨占鎖 , 獲取失敗的線程將會進入等待隊列* @param acquires* @return*/protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);} }

    說明: 從lock方法的源碼可知,每一次都嘗試獲取鎖,而并不會按照公平等待的原則進行等待,讓等待時間最久的線程獲得鎖。

    FairSync類

    FairSync類也繼承了Sync類,表示采用公平策略獲取鎖,其 實現了Sync類中的抽象lock方法,源碼如下:

    static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}/*** 嘗試獲取獨占鎖. 兩種情況:* 1. 鎖未被占用(state = 0 )* 如果等待隊列為空 或當前線程時等待隊列首個出隊線程 則嘗試獲取鎖.* 2. 鎖被占用(state != 0)* 校驗當前線程是否已經擁有獨占鎖,如果是,則記錄重入次數。* @param acquires* @return*/protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {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;}}

    說明:跟蹤lock的源碼可知,當一個線程嘗試獲取鎖時,總是會首先判斷sync隊列(AbstractQueuedSynchronizer中的數據結構)是否有等待時間更長的線程。如果存在,則將線程加入到等待隊列的尾部,實現了公平獲取原則。這是和Nonfair最大的區別,Nonfair每一次都會嘗試去獲取資源,如果此時該資源恰好被釋放,這就造成了不公平的現象,當獲取不成功,再加入到隊列尾部。

    最后

    我們可以通過下圖來深刻的認識公平性和AQS的獲取過程。

    非公平的,或者說默認的獲取方式如下圖所示:

    對于狀態的獲取,可以快速的通過tryAcquire的成功,也就是黃色的Fast路線,也可以由于tryAcquire的失敗,構造節點,進入sync隊列中排序后再次獲取。因此可以理解為Fast就是一個快速通道,當例子中的線程釋放鎖之后,快速的通過Fast通道再次獲取鎖,就算當前sync隊列中有排隊等待的線程也會被忽略。這種模式,可以保證進入和退出鎖的吞吐量,但是sync隊列中過早排隊的線程會一直處于阻塞狀態,造成“饑餓”場景。

    而公平鎖,就是在tryAcquire的調用中顧及當前sync隊列中的等待節點(廢棄fast通道),也就是任意請求都需要按照sync隊列中既有的順序進行,先到先得。這樣很好的確保了公平性。

    回答之前的疑惑

  • ReentrantLock是如何實現鎖管理的。
    加鎖:
  • int c = getState(); if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;} }

    釋放鎖:

    int c = getState() - releases;boolean free = false; if (c == 0) {free = true;setExclusiveOwnerThread(null); } setState(c);return free;

    state狀態量的值如果大于0,則表示鎖被占用,否則以CAS 樂觀鎖的形式將state賦值,從而表示鎖被占用。釋放鎖的過程其實就是state遞減為0的過程,此時不需要同步任何同步手段,是因為只有之前lock成功的線程才能調用unlock方法,所以無需任何同步手段。

    2.ReentrantLock是如何實現重入的。
    通過state的值記錄重入次數,釋放的時候也是通過state值遞減來記錄是否退出所有的重入。

    3.ReentrantLock是如何實現公平鎖與非公平鎖?
    是通過“barge”的形式,快速搶占,搶不到再遵守秩序排隊。

    4.ReentantLock的公平鎖為什么一般情況下性能都比公平鎖查。

    在恢復一個被掛起的線程與該線程真正運行之間存在著嚴重的延遲。

    假設線程A持有一個鎖,并且線程B請求這個鎖。由于所被A持有,因此B將被掛起。當A釋放鎖時,B將被喚醒,因此B會再次嘗試獲取這個鎖。與此同時,如果線程C也請求這個鎖,那么C很可能會在B被完全喚醒之前獲得、使用以及釋放這個鎖。這樣就是一個雙贏的局面:B獲得鎖的時刻并沒有推遲,C更早的獲得了鎖,做完了自己要做的事,并且吞吐量也提高了。

    頻繁的線程切換和線程喚醒睡眠動作將會消耗系統資源:

    當一個線程嘗試加鎖的時候,如果發現鎖被搶占,會睡眠等待喚醒再次嘗試加鎖。如果鎖競爭比較激烈,會造成頻繁的線程切換 線程睡眠與喚醒動作。

    5.我們是否應該拋棄synchronized ?

    雖然 ReentrantLock 是個非常動人的實現,相對 synchronized 來說,它有一些重要的優勢,但是我認為急于把 synchronized 視若敝屣,絕對是個嚴重的錯誤。 java.util.concurrent.lock 中的鎖定類是用于高級用戶和高級情況的工具 。一般來說,除非您對 Lock 的某個高級特性有明確的需要,或者有明確的證據(而不是僅僅是懷疑)表明在特定情況下,同步已經成為可伸縮性的瓶頸,否則還是應當繼續使用 synchronized。

    為什么我在一個顯然“更好的”實現的使用上主張保守呢?因為對于 java.util.concurrent.lock 中的鎖定類來說,synchronized 仍然有一些優勢。比如,在使用 synchronized 的時候,不能忘記釋放鎖;在退出 synchronized 塊時,JVM 會為您做這件事。您很容易忘記用 finally 塊釋放鎖,這對程序非常有害。您的程序能夠通過測試,但會在實際工作中出現死鎖,那時會很難指出原因(這也是為什么根本不讓初級開發人員使用 Lock 的一個好理由。)

    另一個原因是因為,當 JVM 用 synchronized 管理鎖定請求和釋放時,JVM 在生成線程轉儲時能夠包括鎖定信息。這些對調試非常有價值,因為它們能標識死鎖或者其他異常行為的來源。 Lock 類只是普通的類,JVM 不知道具體哪個線程擁有 Lock 對象。而且,幾乎每個開發人員都熟悉 synchronized,它可以在 JVM 的所有版本中工作。在 JDK 5.0 成為標準(從現在開始可能需要兩年)之前,使用 Lock 類將意味著要利用的特性不是每個 JVM 都有的,而且不是每個開發人員都熟悉的。

    6.什么時候選擇用 ReentrantLock 代替 synchronized?

    結構鎖、多個條件變量或者鎖投票。 ReentrantLock 還具有可伸縮性的好處,應當在高度爭用的情況下使用它,但是請記住,大多數 synchronized 塊幾乎從來沒有出現過爭用,所以可以把高度爭用放在一邊。我建議用 synchronized 開發,直到確實證明 synchronized 不合適,而不要僅僅是假設如果使用 ReentrantLock “性能會更好”。請記住,這些是供高級用戶使用的高級工具。(而且,真正的高級用戶喜歡選擇能夠找到的最簡單工具,直到他們認為簡單的工具不適用為止。)。一如既往,首先要把事情做好,然后再考慮是不是有必要做得更快。

    Lock 框架是同步的兼容替代品,它提供了 synchronized 沒有提供的許多特性,它的實現在爭用下提供了更好的性能。但是,這些明顯存在的好處,還不足以成為用 ReentrantLock 代替 synchronized 的理由。相反,應當根據您是否 需要 ReentrantLock 的能力來作出選擇。大多數情況下,您不應當選擇它 —— synchronized 工作得很好,可以在所有 JVM 上工作,更多的開發人員了解它,而且不太容易出錯。只有在真正需要 Lock 的時候才用它。在這些情況下,您會很高興擁有這款工具。

    參看博文

    • ReentrantLock(重入鎖)以及公平性
    • 【JUC】JDK1.8源碼分析之ReentrantLock(三)
    • ReentrantLock實現原理深入探究
    • Java中的鎖
    • 公平鎖與非公平鎖
    • java并發庫 Lock 公平鎖和非公平鎖

    轉載于:https://www.cnblogs.com/boothsun/p/7955907.html

    總結

    以上是生活随笔為你收集整理的ReentrantLock 分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。