溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ReentrantLock (重入鎖) 源碼淺析

發(fā)布時間:2020-07-04 14:31:09 來源:網絡 閱讀:3493 作者:wx5c78c8b1dbb1b 欄目:編程語言

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

三、主要的方法
分析一些常用方法,不會介紹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,我們發(fā)現其實調用的是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();
            // 獲取鎖的狀態(tài)
            int c = getState();
            if (c == 0) {
                // hasQueuedPredecessors 判斷隊列還有沒有其它node,要保證公平
                // 沒有在用cas設置狀態(tài)
                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");
                // 重新設置鎖的狀態(tài)
                setState(nextc);
                return true;
            }
            return false;
        }
    }

非公平實現:NonfairSync,我們可以發(fā)現基本和公平一樣,就沒有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方法,我們發(fā)現其實他也是調用的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(可能發(fā)生了鎖的重入,需要多次釋放)變成0,這個不區(qū)分公平與非公平,首先其實也是調用AQS的release方法,然后AQS在調用子類Sync的tryRelease方法。

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

調用Sync的tryRelease方法

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

⑤、newCondition方法,創(chuàng)建一個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的尾節(jié)點開始往前遍歷,除去空節(jié)點(但是其實只有第一個節(jié)點是空節(jié)點),也就是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 并發(fā)編程的藝術》

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI