從源碼角度理解ReentrantLock(java.util.concurrent.locks.ReentrantLock)

一、ReentrantLock介紹

ReentrantLock重入鎖,能夠實現對同一個資源的重複加鎖,即當前線程重複申請資源的時候,在已經擁有鎖的前提下,不用被阻塞,便可以直接擁有鎖。對於ReentrantLock的理解主要是理解如何實現重入性,公平鎖和非公平鎖對Sync內部類的實現。我們在這裏主要講解公平鎖和非公平鎖,重入性的實現在講解的時候指出。
在使用的時候,官方建議一直跟隨try代碼塊,例如

public void m() {
   lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
        lock.unlock()
     }
 }

這樣可以避免因爲出現異常等原因無法釋放鎖,導致其他線程一直無法獲取鎖。

二、ReentrantLock繼承關係

ReentrantLock的繼承關係及其內部類
該圖顯示了ReentrantLock的繼承關係以及內部實現類。
ReentrantLock實現了Lock接口,表明該鎖是JDK層面的鎖,而不是JVM層面的鎖(synchronized),也可以實現對資源進行加鎖,解鎖等操作。
其中FairSync是公平鎖的實現類,NonfairSync是非公平鎖的實現類。Sync繼承自AbstractQueuedSynchronizer抽象類。

三、Sync

繼承自AbstractQueuedSynchronizer,簡稱AQS,他提供了一個機遇FIFO的隊列,可以用於實現依賴於先進先出(FIFO)等待隊列的阻塞鎖鎖和相關同步器等。

3.1 主要實現的方法

加鎖操作

		/**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         *
         * 執行非公平的tryLock,tryAcquire繼承自子類,
         * 但是兩個都需要非公平的trylock方法
         *
         */
        final boolean nonfairTryAcquire(int acquires) {       // 傳入請求資源的個數
            final Thread current = Thread.currentThread();   // 獲得當前線程
            int c = getState();   //  獲得鎖的狀態
            if (c == 0) {            // 當前沒有被鎖定
                if (compareAndSetState(0, acquires)) {    // 嘗試獲取鎖
                    setExclusiveOwnerThread(current);     //  設置當前擁有獨佔訪問權限的線程
                    return true;     // 加鎖成功,返回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;   //  加鎖失敗
        }

釋放鎖操作

		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);     // 將擁有獨佔權限的線程設置爲null,表示沒有線程擁有獨佔訪問權限
            }
            setState(c);     // 更新鎖狀態
            return free;
        }

裏面的state成員變量在AbstractQueuedSynchronizer抽象類中,表示的是同步的狀態,數值爲多少就表示有多少的鎖。

四、FairSync(公平鎖)

4.1 加鎖

加鎖時直接調用的是lock()方法,lock()方法通過acquire()方法間接的調用tryAcquire()方法進行加鎖。

		/**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();   // 獲得當前線程
            int c = getState();     // 獲得當前的鎖狀態
            if (c == 0) {  // 當前鎖爲空
                if (!hasQueuedPredecessors() &&   // 判斷是否有比當前線程等待更久的線程
                        compareAndSetState(0, acquires)) {     // 如果沒有就使用CAS算法,修改鎖狀態
                    setExclusiveOwnerThread(current);         // 設置擁有獨佔訪問權限的線程是當前線程
                    return true;    //  加鎖成功,返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;                              //  加鎖失敗
        }

在沒有鎖的情況下使用CAS算法進行修改,而在有鎖的情況下直接修改便可,是因爲在有鎖的情況下其他線程無法訪問並修改狀態,而在無鎖狀態下,其他線程也可以訪問並修改鎖的狀態,所以必須使用CAS算法。

4.2 加鎖失敗,進入等待

當在加鎖的時候,會調用java.util.concurrent.locks.AbstractQueuedSynchronizer中的acquire()方法

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&      // 先嚐試加鎖
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))   //  如果加鎖失敗便將該線程加入到等待隊列
            selfInterrupt();   // 兩者均失敗,便終止該線程
    }

4.3 釋放鎖

鎖的釋放使用的是Sync類裏面的tryRelease()方法,與上述的釋放鎖的方式一致。

五、非公平鎖

5.1 加鎖

final void lock() {
     if (compareAndSetState(0, 1))   // 使用CAS算法判斷當前是否已經被鎖住(如果成功交換就代表沒有被鎖住)
        setExclusiveOwnerThread(Thread.currentThread());  // 就設置擁有獨佔權限的線程是當前線程
     else
        acquire(1);    // 將會重新嘗試加鎖,如果依舊加鎖失敗,便加入等待隊列,然後中斷當前線程
 }

非公平鎖在第一步加鎖失敗,重新嘗試加鎖的時候,會調用Sync中的nonfairTryAcquire()方法,該方法上面已經講述過,便不在贅述。

5.2 釋放鎖

鎖的釋放使用的是Sync類裏面的tryRelease()方法

六、其他方法

其他方法均是獲得一些基本信息,直接閱讀就可以看懂

如果文章中有任何錯誤的地方,歡迎指出,謝謝!

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