AbstractQueuedSynchronizer ConditionObject解析

寒武沒有紀發表於2018-09-29

分析過程

// condition佇列第一個節點
private transient Node firstWaiter;
// condition佇列最後一個節點
private transient Node lastWaiter;
// 重新中斷退出等待
private static final int REINTERRUPT =  1;
// 丟擲中斷異常退出等待
private static final int THROW_IE    = -1;

首先來看 ConditionObject. await() 等待方法

// AbstractQueuedSynchronizer.ConditionObject
public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 【為等待佇列新增新節點】
    Node node = addConditionWaiter();
    //【為當前狀態值呼叫釋放方法,返回儲存狀態,在失敗時取消節點並丟擲異常】
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 【判斷等待佇列上的節點是否正在同步佇列上等待獲取鎖】
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this); // 阻塞執行緒
    // 等待後檢查中斷
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // 使用獨佔不可中斷模式獲取鎖 且 中斷模式不是THROW_IE 則設定中斷模式為REINTERRUPT
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters(); //【斷開condition佇列已取消的等待節點】
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode); // 根據中斷模式進行相應處理
}

為等待佇列新增新節點

// AbstractQueuedSynchronizer.ConditionObject
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // 如果最後一個等待是取消狀態則移除
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters(); // 【斷開取消狀態的等待結點】
        t = lastWaiter;
    }
    // 建立狀態為CONDITION的新節點
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node; // condition佇列為空,新增為第一個節點
    else
        t.nextWaiter = node; // 將原有最後一個等待結點的前一個節點指向當前節點
    lastWaiter = node; // 將當前節點設定為condition佇列的最後一個節點
    return node;
}

斷開取消狀態的等待結點

private void unlinkCancelledWaiters() {
    Node t = firstWaiter; // 第一個等待節點
    Node trail = null; // 儲存狀態為CONDITION的遺留節點
    // 從頭開始迴圈condition佇列
    while (t != null) {
        Node next = t.nextWaiter;
        // 如果等待狀態為CONDITION 則將下一個節點設定為null,斷開關聯
        if (t.waitStatus != Node.CONDITION) {
            t.nextWaiter = null;
            // 如果沒有遺留節點,則將下一個節點設定為第一個節點
            if (trail == null)
                firstWaiter = next;
            else
                trail.nextWaiter = next; // 存在遺留節點,將下一個節點設定為遺留節點的下一個節點
        // 遍歷至尾部,將condition佇列最後一個節點指向遺留節點
        if (next == null)
                lastWaiter = trail; 
        }
        else
            trail = t; // 記錄狀態為CONDITION的遺留節點
        t = next; // 迴圈下一個節點
    }
}

為當前狀態值呼叫釋放方法,返回儲存狀態,在失敗時取消節點並丟擲異常

final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        int savedState = getState(); // 獲取同步狀態
        // 釋放鎖
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        // 釋放失敗將節點狀態設定為取消
        if (failed)
            node.waitStatus = Node.CANCELLED; 
    }
}

判斷等待佇列上的節點是否正在同步佇列上等待獲取鎖

final boolean isOnSyncQueue(Node node) {
    // 如果節點等待狀態為CONDITION 或 節點的前驅節點為空,則返回false
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    // 節點存在後繼節點,說明還在同步佇列上等待獲取鎖
    if (node.next != null) // If has successor, it must be on queue
        return true;
    return findNodeFromTail(node); // 從佇列尾部開始查詢當前節點是否處於同步佇列中
}

ConditionObject.signal() 喚醒方法

// AbstractQueuedSynchronizer.ConditionObject
public final void signal() {
    // 判斷是否處於獨佔狀態
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignal(first); // 【執行喚醒操作】
}

執行喚醒操作

private void doSignal(Node first) {
    do {
        // 將頭節點設定為頭節點的下一個節點並判斷是否為空
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null; // 如果為空則將最後一個節點設定為空
        first.nextWaiter = null; // 將頭節點的下一節點設定為空
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

轉換條件佇列節點至同步佇列

final boolean transferForSignal(Node node) {
    // CAS設定等待狀態,設定失敗說明節點已經被取消
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    Node p = enq(node); // 將節點新增至同步佇列
    int ws = p.waitStatus;
    // 如果節點被取消 或 CAS設定等待狀態為SIGNAL失敗
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread); // 喚醒節點所屬執行緒
    return true;
}

主要的等待喚醒方法分析完成。

參考資料

併發程式設計的藝術

相關文章