在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(); // 第二次解鎖
}
}
}
在這個例子中,performTask
和 anotherMethod
都會加鎖,而由於是同一執行緒,所以 lock
會被允許多次加鎖。
5. 鎖的釋放
當執行緒多次加鎖時,每次加鎖都對應一次釋放。只有當釋放次數與加鎖次數相等時,鎖才會真正釋放,允許其他執行緒獲取。
總結
AQS 的可重入性主要是透過 state
變數和 exclusiveOwnerThread
來實現的。它透過跟蹤執行緒加鎖的次數以及鎖的當前擁有者,確保同一個執行緒可以多次進入鎖區域,而不會導致死鎖。這是 Java 中許多同步類(如 ReentrantLock
)的基礎。