併發編程 —— 源碼分析公平鎖和非公平鎖

前言

ReentrantLock 提供了公平鎖和非公平鎖,只須要在構造方法中使用一個 boolean 參數便可。默認非公平鎖。java

今天從源碼層面看看區別和具體實現。ui

1. 類 UML 圖

image.png

ReentrantLock 內部有一個抽象類 Sync,繼承了 AQS。spa

而公平鎖的實現就是 FairSync,非公平鎖的實現就是 NodFairSync線程

兩把鎖的區別在於lock 方法的實現。設計

2. 公平鎖 lock 方法實現

final void lock() {
    acquire(1);
}
複製代碼

調用的是 AQS 的acquire方法,熟悉 AQS 的同窗都知道,AQS 會回調子類的 tryAcquire 方法,看看公平鎖的tryAcquire實現。code

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;
}
複製代碼

說下邏輯:cdn

  1. 獲取 state 變量,若是是 0,說明鎖能夠獲取。
  2. 判斷 AQS 隊列中是否有等待的線程,若是沒有,就使用 CAS 嘗試獲取。獲取成功後,將 CLH 的持有線程修改成當前線程。
  3. 重入鎖邏輯。
  4. 若是失敗,返回 false, AQS 會將這個線程放進隊列,並掛起。

注意上面的第二步:判斷 AQS 隊列中是否有等待的線程blog

這就是公平的體現。繼承

再看看非公平鎖的區別。隊列

3. 非公平鎖 lock 方法實現

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

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}
複製代碼

lock 方法就不一樣了,很衝動的一個方法,直接使用 CAS 獲取鎖,若是成功了,就設置鎖的持有線程爲本身。很快速。因此默認使用了非公平鎖。

若是失敗了,就調用 AQS 的 acquire 方法。固然,咱們看的仍是tryAcquire方法,在上面的代碼中,tryAcquire方法調用了父類SyncnonfairTryAcquire,爲何在父類中呢?

ReentrantLocktryLock方法中,也調用了該方法。由於這個方法是快速返回的。該方法不會讓等待時間久的線程獲取鎖。符合 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) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
複製代碼

該方法相比較公平鎖的 tryAcquire方法,少了一步判斷 AQS 隊列中是否有等待的線程的操做。

他要作的就是直接搶鎖,不讓給隊列裏那些等待時間長的。

搶不到再進入隊列。等待他的前置節點喚醒他。這個過程是公平的。

4. 總結

ReentrantLock中的公平鎖和非公平鎖的區別就在於:調用 lock 方法獲取鎖的時候 要不要判斷 AQS 隊列中是否有等待的線程,公平鎖爲了讓每個線程都均衡的使用鎖,就須要判斷,若是有,讓給他,非公平鎖很霸道,不讓不讓就不讓。

但若是失敗了,進入隊列了,進會按照 AQS 的邏輯來,總體順序就是公平的。

還有個注意的地方就是:ReentrantLocktryLock(無超時機制) 方法使用的非公平策略。符合他的設計。

tryLock(long timeout, TimeUnit unit) 方法則會根據 Sync 的具體實現來調用。不會衝動的調用 nonfairTryAcquire 方法。

相關文章
相關標籤/搜索