併發程式設計(ReentrantLock)

糯米๓發表於2024-04-23

ReentrantLock 是獨佔鎖,每次只能有一個執行緒能獲取到鎖(支援重入)。其他未獲取鎖的執行緒會放入的CLH佇列中,等待當前執行緒喚醒;

主要分為公平鎖和非公平鎖,由內部類FairSyncNoFairSync來實現。主要的區別在於非公平鎖每次都會嘗試競爭,競爭不到鎖才會放入到CLH佇列中

  • NonfairSync類

NonfairSync類繼承了Sync類,表示採用非公平策略獲取鎖,其實現了Sync類中抽象的lock方法,原始碼如下:

// 非公平鎖
static final class NonfairSync extends Sync {
    // 版本號
    private static final long serialVersionUID = 7316153563782823691L;

    // 獲得鎖
    final void lock() {
        if (compareAndSetState(0, 1)) // 比較並設定狀態成功,狀態0表示鎖沒有被佔用
            // 把當前執行緒設定獨佔了鎖
            setExclusiveOwnerThread(Thread.currentThread());
        else // 鎖已經被佔用,或者set失敗
            // 以獨佔模式獲取物件,忽略中斷
            acquire(1); 
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

說明: 從lock方法的原始碼可知,每一次都嘗試獲取鎖,而並不會按照公平等待的原則進行等待,讓等待時間最久的執行緒獲得鎖。

  • FairSync類

FairSync類也繼承了Sync類,表示採用公平策略獲取鎖,其實現了Sync類中的抽象lock方法,原始碼如下:

// 公平鎖
static final class FairSync extends Sync {
    // 版本序列化
    private static final long serialVersionUID = -3000897897090466540L;

    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) { // 狀態為0
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) { // 不存在已經等待更久的執行緒並且比較並且設定狀態成功
                // 設定當前執行緒獨佔
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) { // 狀態不為0,即資源已經被執行緒佔據
            // 下一個狀態
            int nextc = c + acquires;
            if (nextc < 0) // 超過了int的表示範圍
                throw new Error("Maximum lock count exceeded");
            // 設定狀態
            setState(nextc);
            return true;
        }
        return false;
    }
}

說明: 跟蹤lock方法的原始碼可知

主要區別就在於hasQueuedPredecessors方法,先會判斷是否存在等待佇列。

示例:

private ReentrantLock lock = new ReentrantLock();
public void test(){
    lock.lock();
	try {
        ...
    }finally {
       lock.unlock();
    }   
        
}

相關文章