天天看點

教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease

4.1 NonfairSync - 非公平鎖

  • Sync 對象的非公平鎖
  • 教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease

4.1.1 lock

  • 非公平模式的 lock 方法
  • 教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease
  • 若 CAS(已經定義并實作在 AQS 中的 final 方法)state 成功,即擷取鎖成功并将目前線程設定為獨占線程

若 CAS state 失敗,即擷取鎖失敗,則進入 AQS 中已經定義并實作的 Acquire 方法善後

這裡的 lock 方法并沒有直接調用 AQS 提供的 acquire 方法,而是先試探地使用 CAS 擷取了一下鎖,CAS 操作失敗再調用 acquire 方法。這樣設計可以提升性能。因為可能很多時候我們能在第一次試探擷取時成功,而不需要再經過 acquire => tryAcquire => nonfairAcquire 的調用鍊。

4.1.2 tryAcquire

教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease

其中真正的實作 nonfairTryAcquire 就定義在其父類 Sync 中。下一節分析。

4.2 FairSync - 公平鎖

隻實作 lock 和 tryAcquire 兩個方法

4.2.1 lock

lock 方法加鎖成功,直接傳回,是以可以繼續執行業務邏輯。

  • 公平模式的 lock
  • 教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease
  • 直接調用 acquire,而沒有像非公平模式先試圖擷取,因為這樣可能導緻違反“公平”的語義:在已等待在隊列中的線程之前擷取了鎖。

acquire 是 AQS 的方法,表示先嘗試獲得鎖,失敗之後進入同步隊列阻塞等待。

4.2.2 tryAcquire

  • 該方法是 AQS 在 acquire 方法中留給子類去具體實作的
  • 教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease

公平模式

不要授予通路權限,除非遞歸調用或沒有等待線程或是第一個調用的。

protected final boolean tryAcquire(int acquires) {
    // 擷取目前的線程
    final Thread current = Thread.currentThread();
    // 擷取 state 鎖的狀态(volatile 讀語義)
    int c = getState();
    // state == 0 => 尚無線程擷取鎖
    if (c == 0) {
        // 判斷 AQS 的同步對列裡是否有線程等待
        if (!hasQueuedPredecessors() &&
            // 若沒有則直接 CAS(保證原子性,線程安全) 擷取鎖
            compareAndSetState(0, acquires)) {
            // 擷取鎖成功,設定獨占線程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 已經擷取鎖的是否為目前的線程?
    else if (current == getExclusiveOwnerThread()) {
        // 鎖的重入, 即 state 加 1
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        // 已經擷取 lock,是以這裡不考慮并發       
        setState(nextc);
        return true;
    }
    return false;
}      

和Sync#nonfairTryAcquire類似,唯一不同的是當發現鎖未被占用時,使用 hasQueuedPredecessors 確定了公平性。

hasQueuedPredecessors

判斷目前線程是不是屬于同步隊列的頭節點的下一個節點(頭節點是釋放鎖的節點):

  • 是(傳回false),符合FIFO,可以獲得鎖
  • 不是(傳回true),則繼續等待
public final boolean hasQueuedPredecessors() {
    // 這種方法的正确性取決于頭在尾之前初始化和頭初始化。如果目前線程是隊列中的第一個線程,則next是精确的
    Node t = tail; // 按反初始化順序讀取字段
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}      

5 nonfairTryAcquire

執行非公平的 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;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded"); 
        setState(nextc);
        return true;
    }
    return false;
}      

無參的 tryLock 調用的就是此方法

6 tryLock

6.1 無參

Lock 接口中定義的方法。

  • 僅當鎖在調用時未被其他線程持有時,才擷取鎖
  • 教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease
  • 如果鎖未被其他線程持有,則擷取鎖,并立即傳回值 true,将鎖持有計數設定為1。即使這個鎖被設定為使用公平的排序政策,如果鎖可用,調用 tryLock() 也會立即獲得鎖,不管其他線程是否正在等待鎖。這種妥協行為在某些情況下是有用的,雖然它破壞了公平。如果想為這個鎖執行公平設定,那麼使用 tryLock(0, TimeUnit.SECONDS),這幾乎是等價的(它還可以檢測到中斷)。

如果目前線程已經持有該鎖,那麼持有計數将增加1,方法傳回true。

如果鎖被另一個線程持有,那麼這個方法将立即傳回值false。

6.2 有參

  • 提供了逾時時間的入參,在時間内,仍沒有得到鎖,會傳回 false
  • 教面試官ReentrantLock源碼(下)5 nonfairTryAcquire6 tryLock7 tryRelease
  • 其中的 doAcquireNanos 已經實作好在 AQS 中。

7 tryRelease

釋放鎖,對于公平和非公平鎖都适用

protected final boolean tryRelease(int releases) {
    // 釋放 releases (由于可重入,這裡的 c 不一定直接為 0)
    int c = getState() - releases;
    // 判斷目前線程是否是擷取獨占鎖的線程
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 鎖已被完全釋放
    if (c == 0) {
        free = true;
        // 無線程持有獨占鎖,是以置 null
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}      

繼續閱讀