知識圖譜整理之Java基礎ReentrantLock

ReentrantLock介紹

ReentrantLock也是在JUC併發包下的,實現可重入鎖的機制。也是在AQS基礎之上的,在上一篇AQS的講解基礎下,理解這篇會比較的簡單。知識圖譜整理之Java基礎AbstractQueuedSynchronizer

ReentrantLock的概覽

要了解這個類,主要還是從它的使用方法上入手,因爲這個是的數據結構基礎是在AQS之上的,所以內部不再重寫數據結構,主要關注點還是在於這個類的方法。

在這個講輸出的年代,ReentrantLock是如何實現輸出的呢?他其實是內部維護了一個繼承AQS的類,然後在通過獲取鎖是否公平的方式分爲了兩種策略,即公平鎖和非公平鎖。首先我們來看下這個基礎類Sync源碼。

源碼解讀

Sync內部類源碼

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        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;
        }

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

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
  • 首先可以看到這個類是個抽象類,所以可以確定它肯定有子類實現。
  • nonfairTryAcquire方法比較關鍵,是非公平嘗試獲取鎖,裏面的邏輯其實就比較簡單了,無非是先獲取state變量的值,通過CAS方式修改,如果修改成功就代表獲取到了鎖標識。如果失敗,會判斷當前線程是否爲已獲取到鎖標識的線程,如果是則state+1來代表重入次數。
  • tryRelease的邏輯就不多說了,就是關於state變量操作
  • 這個類主要是這兩個方法比較重要,其他的等我們用到了在做深入

NonfairSync內部類源碼

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
  • 這裏就實現了Sync的lock方法,也比較的簡單
  • 如果獲取鎖不成功,我們看到調用了acquire,這是AQS的方法,還記得這裏的操作麼?不記得可以看看AQS的,大概就是調用了tryAcquire方法,如果不行就放進同步等待隊列。
  • tryAcquire就是調用了Sync的nonfairTryAcquire方法,這樣整體就串起來了

FairSync內部類源碼

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

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

        /**
         * 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)) {
                    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方法,很簡單就是調用了AQS的acquire,在調用FairSync實現的tryAcquire方法
  • tryAcquire是公平鎖的關鍵,就是在state爲0時,即可以爭取鎖時,哪些線程可以爭。
  • hasQueuedPredecessors方法很關鍵,這個方法作用是查詢是否有任何線程在等待獲取比當前線程更長的。
hasQueuedPredecessors方法源碼
public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
  • 第一個條件 h != t代表如果當前沒有任何其他競爭線程在隊列中的,直接返回false。h==t代表沒有其他在等待的競爭線程
  • ((s = h.next) == null || s.thread != Thread.currentThread())這個條件判斷是否頭結點的下一個節點的線程是否是當前線程。這裏要結合這個方法的上層,是判斷如果非則繼續執行,所以整體邏輯就是如果是當前線程,則繼續往下執行。

鎖如何對外輸出

從構造方法開始:

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

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

其實就是用了策略模式,確定鎖策略,實現交給對應策略子類。

今日總結

今天閱讀了關於ReentrantLock的源碼,在理解了AQS的基礎上,閱讀起來比較簡單,再來曬下今天的知識圖譜輸出:知識圖譜

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