ReentrantLock原始碼分析

辣雞小籃子發表於2020-09-06

ReentrantLock原始碼分析

ReentrantLock是獨享鎖,同時是基於AQS實現的,因此它內部肯定是通過自定義AQS獨佔模式下的同步器來實現獨享鎖,該同步器需要重寫AQS提供的tryAcquire()和tryRelease()方法,只需要告訴AQS嘗試獲取同步資源和釋放同步資源是否成功即可。

AQS子類需要定義以及維護同步狀態的值,在ReentrantLock中使用state為0表示同步資源沒有被執行緒所持有,使用state不為0表示同步資源已經被執行緒所持有。

ReentrantLock有公平和非公平兩種模式,公平模式是指多執行緒按照申請鎖的順序來獲取鎖,非公平模式是指多執行緒並非按照申請鎖的順序來獲取鎖。


ReentrantLock的結構

public class ReentrantLock implements Lock, java.io.Serializable {
    
    private final Sync sync;

    /**
     * 抽象同步器(AQS獨佔模式)
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
       // ......
    }
    
    /**
     * 非公平同步器
     */
    static final class NonfairSync extends Sync {
       // ......
    }

    /**
     * 公平同步器
     */
    static final class FairSync extends Sync {
      // ......
    }

    public ReentrantLock() {
        sync = new NonfairSync();
    }

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

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

    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

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

    public void unlock() {
        sync.release(1);
    }
  
    // 其它省略
}

可以發現ReentrantLock中定義了一個抽象同步器(Sync)、非公平同步器(NonfairSync)、公平同步器(FairSync),同時非公平同步器和公平同步器都繼承抽象同步器。

同時ReentrantLock中存在一個全域性的抽象同步器屬性(sync),通過ReentrantLock的構建方法來進行初始化,並通過引數來指定是使用公平同步器還是非公平同步器,預設情況下是使用非公平同步器。

同時ReentrantLock中的核心方法,如lock()加鎖方法,是呼叫抽象同步器宣告的lock()方法,unlock()解鎖方法,是呼叫AQS的release()方法,而tryLock()方法,是呼叫抽象同步器提供的nonfairTryAcquire()方法。


剖析抽象同步器

/**
 * 抽象同步器(AQS獨佔模式)
 */ 
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    /**
     * 宣告瞭lock()方法,用於加鎖
     * ReentrantLock的lock()方法會呼叫抽象同步器宣告的lock()方法
     */ 
    abstract void lock();

    /**
     * 用於嘗試獲取鎖,如果獲取成功則返回true,並修改同步狀態的值,否則返回false
     * ReentrantLock的tryLock()方法會呼叫抽象同步器提供的nonfairTryAcquire()方法
     */
    final boolean nonfairTryAcquire(int acquires) {
        // 獲取當前執行緒
        final Thread current = Thread.currentThread();
        // 獲取同步狀態
        int c = getState();
        // 如果同步狀態為0,表示鎖沒有被執行緒所持有
        if (c == 0) {
            // 通過CAS將同步狀態設定為1,獲取鎖
            if (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;
    }

    /**
     * 重寫AQS的tryRelease()方法,用於嘗試釋放鎖,如果釋放成功,則返回true,並修改同步狀態的值,否則返回false
     */ 
    protected final boolean tryRelease(int releases) {
  		// 獲取鎖釋放後剩餘的同步資源
        int c = getState() - releases;
        // 如果擁有鎖的執行緒並非當前執行緒,則直接丟擲異常(要求誰加的鎖只能由誰進行解鎖)
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        
        boolean free = false;
        // 如果鎖釋放後,state的值為0,則返回true,否則返回false(說明執行緒加了不止一把鎖,然後又沒有全釋放)
        if (c == 0) {
            free = true;
            // 清空擁有同步資源的執行緒
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

    /**
     * 判斷當前執行緒是否持有鎖
     */
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    /**
     * 返回Condition例項,在某些場景下可以進行阻塞與喚醒
     */
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

	/**
	 * 獲取擁有鎖的執行緒
	 */
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    /**
     * 返回當前執行緒擁有鎖的個數
     */
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    /**
     * 判斷鎖是否已經被執行緒所持有
     */
    final boolean isLocked() {
        return getState() != 0;
    }

}

抽象同步器宣告的lock()方法用於加鎖,該方法需要由抽象同步器的子類,也就是非公平同步器和公平同步器來實現。

同時抽象同步器中提供了nonfairTryAcquire()方法,該方法會在ReentrantLock的tryLock()方法以及非公平同步器的tryAcquire()方法中被呼叫。

tryAcquire()方法由抽象同步器的子類來重寫,而tryRelease()方法由抽象同步器來重寫。


剖析非公平同步器

/**
 * 非公平同步器(繼承抽象同步器)
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * 實現抽象同步器宣告的lock()方法
     */
    final void lock() {
        // 直接通過CAS嘗試獲取鎖,如果獲取成功則將當前執行緒設定為擁有同步資源的執行緒,然後直接返回,否則呼叫AQS的acquire()方法
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    /**
     * 重寫AQS的tryAcquire()方法,直接呼叫抽象同步器的nonfairTryAcquire()方法
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

非公平模式下的ReentrantLock的加鎖,將會呼叫非公平同步器的lock()方法,該方法先通過CAS嘗試獲取鎖,當獲取失敗時才會呼叫AQS的acquire()方法,該方法又會呼叫非公平同步器的tryAcquire()方法。

非公平模式下的ReentrantLock的解鎖,將會呼叫AQS的release()方法,該方法又會呼叫抽象同步器提供的tryRelease()方法。


剖析公平同步器

/**
 * 公平的同步器(繼承抽象同步器)
 */
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    /**
     * 實現抽象同步器宣告的lock()方法
     */
    final void lock() {
        // 直接呼叫AQS的acquire()方法
        acquire(1);
    }
    
	/**
     * 重寫AQS的tryAcquire()方法
     */
    protected final boolean tryAcquire(int acquires) {
        // 獲取當前執行緒
        final Thread current = Thread.currentThread();
        // 獲取同步狀態
        int c = getState();
        // 如果同步狀態為0,同時等待佇列中頭節點的後繼節點封裝的執行緒是當前執行緒,那麼才會通過CAS將同步狀態設定為1,表示獲取鎖(這個判斷就是用於保證公平鎖的)
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current); // 將擁有同步資源的執行緒設定為當前執行緒
                return true;
            }
        }
        // 如果同步狀態不為0,也就是鎖已經被執行緒所持有,同時如果當前執行緒就是擁有鎖的執行緒,它還嘗試獲取鎖,那麼就累加同步狀態的值,然後返回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;
    }
}

公平模式下的ReentrantLock的加鎖,將會呼叫公平同步器的lock()方法,該方法直接呼叫AQS的acquire()方法,該方法內部又呼叫公平同步器的tryAcquire()方法。

公平模式下的ReentrantLock的解鎖,將會呼叫AQS的release()方法,該方法又會呼叫抽象同步器提供的tryRelease()方法。


非公平模式下的總結

1.當執行緒要獲取鎖時,可以直接呼叫lock()和tryLock()方法。

2.如果呼叫lock()方法,那麼將會呼叫非公平同步器的lock()方法,該方法會先通過CAS嘗試獲取鎖,如果獲取鎖成功則直接返回,否則將會呼叫AQS的acquire()方法。

3.AQS的acquire()方法又會呼叫非公平同步器的tryAcquire()方法,該方法直接呼叫抽象同步器的nonfairTryAcquire()方法,如果同步狀態的值為0,則通過CAS嘗試獲取鎖,如果獲取鎖成功則返回true,否則返回false,同時如果同步狀態的值不為0,同時當前執行緒就是擁有鎖的執行緒,那麼允許它繼續進行加鎖,然後累加同步狀態的值,這種情況也會返回true。

4.如果呼叫tryLock()方法,那麼將會呼叫抽象同步器的nonfairTryAcquire()方法。

5.當執行緒要釋放鎖時,將會呼叫ReentrantLock的unlock()方法,該方法直接呼叫AQS的release()方法,該方法又會呼叫抽象同步器的tryRelease()方法,如果執行緒釋放鎖後,同步狀態的值為0,則返回true,否則說明執行緒加了不止一把鎖,那麼會更新同步狀態的值,然後返回false,只有當執行緒把它加的鎖都釋放後,tryRelease()方法才會返回true。


公平模式下的總結

1.當執行緒要獲取鎖時,可以直接呼叫lock()和tryLock()方法。

2.如果呼叫lock()方法,那麼將會呼叫公平同步器的lock()方法,該方法直接呼叫AQS的acquire()方法。

3.AQS的acquire()方法又會呼叫公平同步器的tryAcquire()方法,該方法中只有當同步狀態的值為0,同時等待佇列中的頭節點的後繼節點封裝的執行緒是當前執行緒時,才會通過CAS嘗試獲取鎖,如果獲取鎖成功則返回true,否則返回false,同時如果同步狀態的值不為0,同時當前執行緒就是擁有鎖的執行緒,那麼允許它繼續進行加鎖,然後累加同步狀態的值,這種情況也會返回true。

4.如果呼叫tryLock()方法,那麼將會呼叫抽象同步器的nonfairTryAcquire()方法。

5.當執行緒要釋放鎖時,將會呼叫ReentrantLock的unlock()方法,該方法直接呼叫AQS的release()方法,該方法又會呼叫抽象同步器的tryRelease()方法,如果執行緒釋放鎖後,同步狀態的值為0,則返回true,否則說明執行緒加了不止一把鎖,那麼會更新同步狀態的值,然後返回false,只有當執行緒把它加的鎖都釋放後,tryRelease()方法才會返回true。


FAQ

關於ReentrantLock的tryLock()方法

ReentrantLock的tryLock()方法是非公平的,因為無論在什麼模式下,ReentrantLock的tryLock()方法總是呼叫抽象同步器的nonfairTryAcquire()方法,因此當執行緒釋放鎖時,需要喚醒離頭節點最近的同時等待狀態不為CANCELLED的後繼節點,然後在該節點嘗試獲取鎖之前,其他執行緒直接呼叫了tryLock()方法獲取了鎖,那麼被喚醒的這個執行緒又只能再進入阻塞狀態,這就是非公平的體現。

關於獲取了鎖的執行緒能否再進行加鎖?

是可以的,因為無論在非公平模式下還是公平模式下,tryAcquire()方法當中都會有這麼一個判斷,也就是如果當前同步狀態的值不為0,表示鎖已經被執行緒所持有,同時當前執行緒就是擁有鎖的執行緒,那麼允許它繼續進行加鎖,然後累加同步狀態的值,這種情況方法也會返回true,同時在抽象同步器重寫的tryRelease()方法,如果執行緒釋放鎖後,同步狀態的值為0,則返回true,否則說明執行緒加了不止一把鎖,那麼會更新同步狀態的值,然後返回false,只有當執行緒把它加的鎖都釋放後,tryRelease()方法才會返回true。

非公平鎖是如何實現非公平的?

主要體現在非公平同步器的lock()方法,當執行緒要進行加鎖時,並沒有直接呼叫AQS的acquire()方法,而是先通過CAS嘗試獲取鎖,因此當執行緒釋放鎖時,需要喚醒離頭節點最近的同時等待狀態不為CANCELLED的後繼節點,然後在該節點嘗試獲取鎖之前,其他執行緒直接呼叫了lock()方法獲取了鎖,那麼被喚醒的這個執行緒又只能再進入阻塞狀態,這就是非公平的體現。

公平鎖是如何實現公平的?

主要體現在公平同步器的lock()和tryAcquire()方法,首先lock()方法直接呼叫AQS的acquire()方法,並沒有像非公平同步器的lock()方法一樣,先通過CAS嘗試獲取鎖,然後在tryAcquire()方法中,只有當同步狀態的值為0,同時等待佇列中的頭節點的後繼節點封裝的執行緒是當前執行緒時,才會通過CAS嘗試獲取鎖,因此當執行緒釋放鎖時,需要喚醒離頭節點最近的同時等待狀態不為CANCELLED的後繼節點,然後在該節點嘗試獲取鎖之前,其他執行緒呼叫了lock()方法進行加鎖,由於lock()方法直接呼叫AQS的acquire()方法,然後acquire()方法又呼叫公平同步器的tryAcquire()方法,雖然判斷到當前同步狀態的值為0,但是當前執行緒並不是等待佇列中頭節點的後繼節點封裝的執行緒,因此該執行緒也只能封裝成Node節點,然後加入到等待佇列當中。

相關文章