1. 程式人生 > >ReentrantLock (重入鎖) 源碼淺析

ReentrantLock (重入鎖) 源碼淺析

protect shel http 構造方法 ner lock int nds unless

一、ReentrantLock簡介
ReentrantLock重入鎖,顧名思義,就是支持重入的鎖,它表示能夠支持一個線程對資源的重復加鎖;我們之前學習過Synchronized鎖,它也是支持重入的一種鎖,參考我的另一篇Synchronized 鎖的實現原理與應用,Synchronized支持隱式的重入鎖,比如遞歸方法,在方法運行時,執行線程在獲取到了鎖之後仍能連續多次地獲取鎖;ReentrantLock雖然不能隱式重入,但是獲取到鎖的線程多次調用lock方法,不會阻塞進入同步隊列;除此之外在獲取鎖時支持公平或者非公平的選擇。
二、主要成員和結構圖
①、ReentrantLock關系圖
技術分享圖片
②、Sync是ReentrantLock的內部類,繼承AQS
技術分享圖片
③、FairSync公平的鎖實現,也是ReentrantLock的內部類,繼承Sync
技術分享圖片
④、NonfairSync非公平的鎖實現,也是ReentrantLock的內部類,繼承Sync
技術分享圖片

三、主要的方法
分析一些常用方法,不會介紹AQS,AQS的一些方法參考我的這一篇文章
①、構造方法,我們可以看出默認的無參是非公平鎖,有參構造true表示公平,false表示非公平。

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

②、lock()獲取鎖,其實就是把state從0變成n(重入鎖可以累加)。
實際調用的是sync的lock方法,分公平和非公平。

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

公平實現:FairSync,我們發現其實調用的是acquire,其實這個是AQS的acquire,然後aqs的acquire的方法裏面又會調用tryAcquire方法,因為這個方法需要同步組件自己去實現,所以ReentrantLock裏面重寫了AQS的tryAcquire方法,所以我們獲取到鎖就會返回true,沒有就會返回false;然後沒有獲取到鎖的線程就交給AQS去處理。

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) {
                // hasQueuedPredecessors 判斷隊列還有沒有其它node,要保證公平
                // 沒有在用cas設置狀態
                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,我們可以發現基本和公平一樣,就沒有hasQueuedPredecessors方法,沒有遵循FIFO隊列的模式,而是不管隊列有沒有node,自己都可以去獲取鎖,不需要排隊

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

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

②、lockInterruptibly支持中斷的獲取鎖,其實是調用了AQS的lockInterruptibly方法,在AQS方法裏面又回去調用tryAcquire方法,這個方法在上面已經解釋過了。

public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

AQS的lockInterruptibly方法

public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

③、tryLock(long timeout, TimeUnit unit),支持中斷,並且在這個基礎上增加了超時設置,其實也是調用了AQS的tryAcquireNanos方法,我們發現其實他也是調用的tryAcquire方法。

 public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

AQS的tryAcquireNanos方法

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }

④、unlock釋放鎖,其實就是把state從n(可能發生了鎖的重入,需要多次釋放)變成0,這個不區分公平與非公平,首先其實也是調用AQS的release方法,然後AQS在調用子類Sync的tryRelease方法。

public void unlock() {
        sync.release(1);
    }

調用Sync的tryRelease方法

protected final boolean tryRelease(int releases) {
            // 獲取鎖的狀態
            int c = getState() - releases;
            // 獲得鎖的線程才能釋放鎖
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 直到鎖的狀態是0,說明鎖釋放成功,因為有重入鎖
            // 說明我們在一個線程裏面調用幾次lock,就要調用幾次unlock,才能最終釋放鎖
            if (c == 0) {
                free = true;
                // 釋放線程的擁有者
                setExclusiveOwnerThread(null);
            }
            // 設置鎖的狀態
            setState(c);
            return free;
        }

⑤、newCondition方法,創建一個newCondition。

public Condition newCondition() {
        return sync.newCondition();
    }

⑥、getHoldCount方法,獲取當前線程獲得鎖的個數。

public int getHoldCount() {
        return sync.getHoldCount();
    }
        final int getHoldCount() {
            // 當前線程是否獲取到鎖
            return isHeldExclusively() ? getState() : 0;
        }

⑦、isHeldByCurrentThread方法,當前線程是否獲取到鎖。

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

⑧、isLocked方法,是否有線程獲取到了鎖。

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

⑨、getOwner方法,獲取取得鎖的線程。
⑩、getQueueLength方法,獲取同步隊列的數量。

public final int getQueueLength() {
    // 從aqs的尾節點開始往前遍歷,除去空節點(但是其實只有第一個節點是空節點),也就是thread != null
        int n = 0;
        for (Node p = tail; p != null; p = p.prev) {
            if (p.thread != null)
                ++n;
        }
        return n;
    }

四、總結

學習ReentrantLock,我們主要需要了解它,公平和非公平的實現,以及重入鎖的獲取與釋放的流程,還有最重要的就是要了解AQS,這是實現重入鎖的基礎,因為ReentrantLock只是實現了AQS獲取鎖和釋放鎖制定的模板方法的語義,所以要理解ReentrantLock獲取鎖成功和失敗具體都做了什麽邏輯,和AQS的實現是離不開的。
可以參考我的這一篇AQS的文章。
參考 《Java 並發編程的藝術》

ReentrantLock (重入鎖) 源碼淺析