ReentrantLock實現原理分析

類圖

ReentrantLock相關類圖:

這裏寫圖片描述

  • AbstractOwnableSynchronizer類保持和獲取獨佔線程。

  • AbstractQueuedSynchronizer,繼承自AbstractOwnableSynchronizer,簡稱AQS,基於FIFO(First Input First Output)隊列的實現。以虛擬隊列的方式管理線程的鎖獲取與鎖釋放,以及各種情況下的線程中斷。提供了默認的同步實現,但是獲取鎖和釋放鎖的實現定義爲抽象方法,由子類實現。目的是使開發人員可以自由定義獲取鎖以及釋放鎖的方式。

  • Sync是ReentrantLock的內部抽象類,繼承自AbstractQueuedSynchronizer,實現了簡單的獲取鎖和釋放鎖。NonfairSync和FairSync分別表示“非公平鎖”和“公平鎖”,都繼承於Sync,並且都是ReentrantLock的內部類。

  • ReentrantLock實現了Lock接口的lock-unlock方法,根據fair參數決定使用NonfairSync還是FairSync。

AQS

ReentrantLock實現的前提就是AbstractQueuedSynchronizer,簡稱AQS,是java.util.concurrent的核心,CountDownLatch、FutureTask、Semaphore、ReentrantLock等都有一個內部類是這個抽象類的子類。

Node

Node是AQS的內部類,是對每一個訪問同步代碼的線程的封裝。不僅包括了需要同步的線程,而且也包含了每個線程的狀態,比如等待解除阻塞,等待條件喚醒,已經被取消等等。同時Node還關聯了前驅和後繼,即prev和next。

多個Node連接起來成爲了虛擬隊列(因爲不存在真正的隊列容器將每個元素裝起來所以說是虛擬的,我把它稱爲release隊列,意思是等待釋放),其實就是一個有頭有尾的雙向鏈表結構:

這裏寫圖片描述

state

是AQS的一個成員變量,用來記錄鎖的持有情況:

  • 沒有線程持有鎖的時候,state爲0

  • 當某個線程獲取鎖時,state的值增加,具體增加多少開發人員可自定義,默認爲1,表示該鎖正在被一個線程佔有。

  • 當某個已經佔用鎖的線程再次獲取到鎖時,state再增長,此爲重入鎖

  • 當佔有鎖的線程釋放鎖時,state也要減去當初佔有時傳入的值,默認爲1。

多個線程競爭鎖的時候,state必須通過CAS進行設置,這樣才能保證鎖只能有一個線程持有。

Sync

NonfairSync和FairSync分別表示“非公平鎖”和“公平鎖”,都繼承於Sync,並且都是ReentrantLock的內部類。

我們來看NonfairSync獲取鎖的代碼:

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

        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) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

還有FairSync獲取鎖的代碼:

        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;
        }

其實差別就在於NonfairSync獲取鎖時比FairSync中少了一個判斷!hasQueuedPredecessors()hasQueuedPredecessors()中判斷了是否存在等待隊列,導致公平鎖和非公平鎖的差異如下:

  • 公平鎖:公平鎖講究先來先到,線程在獲取鎖時,如果這個鎖的等待隊列中已經有線程在等待,那麼當前線程就會進入等待隊列中

  • 非公平鎖:不管是否有等待隊列,如果可以獲取鎖,則立刻佔有鎖對象

lock()與unlock()

lock()

流程圖(圖中的Node0和Node1在源代碼中不存在,是爲了方便說明清楚才添加的別稱):

這裏寫圖片描述

unlock()

流程圖:

這裏寫圖片描述

參考:
1.ReentrantLock實現原理深入探究
2.ReentrantLock的lock-unlock流程詳解
3.輕鬆學習java可重入鎖(ReentrantLock)的實現原理
4.ReentrantLock解析
5.深度解析Java8 – AbstractQueuedSynchronizer的實現分析(上)
6.ReentrantLock(重入鎖)以及公平性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章