首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

深入解析 Condition 的 await 與 signal 機制

     編輯:Java 綜合教學

Condition 的 await-signal 流程詳解

上一篇文章介紹了 ReentrantLock 的 lock-unlock 流程,本篇將深入探討 Condition 的 await 與 signal 機制。

Condition 類結構

Condition 類圖

  • Condition 接口定義了多種 await 方式與兩個通知方法(signal、signalAll)。
  • ConditionObject 實現了 Condition 接口,是 AbstractQueuedSynchronizer (AQS) 的內部類。
  • ReentrantLock 的 newCondition() 方法會返回與該鎖實例關聯的 Condition 對象。

與釋放隊列(release queue)類似,Condition 隊列也是一個虛擬隊列,節點之間透過 nextWaiter 關聯。因為 Condition 節點必須轉為釋放節點才能解除阻塞,所以不需要 prevWaiter 指針。

整體流程大致如下:調用 await 的線程會進入 Condition 隊列;調用 signal 的線程會從 firstWaiter 開始尋找未取消的節點,將其移入釋放隊列;隨後當 signal 線程調用 await 或 unlock 時執行 release 方法,才有機會喚醒該節點。相比 lock-unlock,正常流程較簡單,但中斷處理更為複雜。

await() 至阻塞的流程

await 流程圖

該過程可分為三個步驟。在阻塞線程前,需判斷 Condition 節點是否已在釋放隊列中。若已在隊列,則無需阻塞,可直接參與鎖競爭。關鍵邏輯見 isOnSyncQueue(Node node) 方法:

final boolean isOnSyncQueue(Node node) {
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    if (node.next != null)
        return true;
    return findNodeFromTail(node);
}

signal() 流程

signal 流程圖

signal 方法較簡單:從 firstWaiter 開始,找到首個未取消的節點加入釋放隊列。即使節點最初未取消,入隊時也可能被取消,因此代碼做了特殊處理。核心邏輯在 transferForSignal(Node node):

final boolean transferForSignal(Node node) {
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
    Node p = enq(node);
    int ws = p.waitStatus;
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

signal 僅修改節點狀態,並不直接喚醒線程。喚醒需待後續調用 await() 或 unlock() 時執行 release 方法,對釋放隊列中的節點解除阻塞。

await() 線程被喚醒後的流程

await 喚醒後流程

signalAll 與 signal 類似,只是將 Condition 隊列中所有節點全部移入釋放隊列。中斷處理的詳細流程圖將在後續文章補充。

參考資料:怎麼理解 Condition

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved