[Java併發]AQS的可重入性

Duancf發表於2024-10-03

在Java中,AQS(AbstractQueuedSynchronizer,抽象佇列同步器)透過設計一個獨佔和共享的同步機制,提供了可重入鎖的實現。AQS 的可重入性主要依賴於它對執行緒狀態的跟蹤。具體來說,可重入性是指同一個執行緒在獲得鎖之後可以多次進入(加鎖多次),而不引發死鎖。這是透過一個“重入計數器”來實現的。

下面是AQS實現可重入性的核心機制:

1. 執行緒持有狀態(state)

AQS 使用一個 state 變數來表示鎖的持有狀態。在獨佔鎖(如 ReentrantLock)的情況下,state 變數記錄鎖被持有的次數。AQS 的設計允許同一個執行緒多次獲取鎖,每次獲取鎖時,state 變數會遞增,而每次釋放鎖時,state 變數會遞減,直到 state 變為 0 時,鎖才會真正釋放。

2. 當前持有鎖的執行緒

AQS 透過內部的一個執行緒引用 exclusiveOwnerThread 來跟蹤當前持有鎖的執行緒。當一個執行緒嘗試獲取鎖時,AQS 會檢查當前執行緒是否已經持有鎖(即 exclusiveOwnerThread == currentThread)。如果是同一個執行緒,則允許該執行緒再次獲取鎖,表示“可重入”。

3. 可重入的判斷過程

  • 當一個執行緒第一次獲取鎖時,AQS 會將 exclusiveOwnerThread 設定為該執行緒,並將 state 從 0 設定為 1。
  • 如果同一執行緒再次嘗試獲取鎖,AQS 看到 exclusiveOwnerThread 已經是當前執行緒,於是允許鎖的重入,並將 state 遞增。
  • 當執行緒釋放鎖時,AQS 會減少 state 的值。只有當 state 減為 0 時,AQS 才會將 exclusiveOwnerThread 置為 null,表示鎖已完全釋放。

4. 程式碼示例

ReentrantLock 為例,ReentrantLock 是基於 AQS 實現的可重入鎖:

class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        lock.lock(); // 第一次加鎖
        try {
            // 執行任務
            anotherMethod(); // 同一執行緒可以再次加鎖
        } finally {
            lock.unlock(); // 第一次解鎖
        }
    }

    public void anotherMethod() {
        lock.lock(); // 第二次加鎖
        try {
            // 執行其他任務
        } finally {
            lock.unlock(); // 第二次解鎖
        }
    }
}

在這個例子中,performTaskanotherMethod 都會加鎖,而由於是同一執行緒,所以 lock 會被允許多次加鎖。

5. 鎖的釋放

當執行緒多次加鎖時,每次加鎖都對應一次釋放。只有當釋放次數與加鎖次數相等時,鎖才會真正釋放,允許其他執行緒獲取。

總結

AQS 的可重入性主要是透過 state 變數和 exclusiveOwnerThread 來實現的。它透過跟蹤執行緒加鎖的次數以及鎖的當前擁有者,確保同一個執行緒可以多次進入鎖區域,而不會導致死鎖。這是 Java 中許多同步類(如 ReentrantLock)的基礎。

相關文章