java併發編程學習之Condition-分析(二)

ConditionObject

Condition在ReentrantLock中,其實是建立AQS的ConditionObject對象,主要的成員變量有Node類型的firstWaiter和lastWaiter,做爲頭節點和尾節點,是單向鏈表。當調用await時,加入隊列,signal時,加入到AQS的阻塞隊列。node

clipboard.png

await方法

把節點移到Condition隊列後掛起less

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)) {//要麼中斷,要麼進入阻塞隊列,退出while循環
        LockSupport.park(this);//掛起
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)//中斷過,就跳出循環
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)//是否被中斷。acquireQueued以前講過
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled若是尾節點不爲空
        unlinkCancelledWaiters();//將不是CONDITION狀態的移除出去
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);//從新中斷
}

addConditionWaiter,若是尾節點不在隊列裏,先移除已取消的節點,添加一個節點成爲尾節點ui

private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.若是尾節點不爲空,可是狀態不是CONDITION,說明已取消,不想在Condition的隊列裏,就移除
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();//將不是CONDITION狀態的移除出去
        t = lastWaiter;
    }
    Node node = new Node(Thread.currentThread(), Node.CONDITION);//建立狀態是CONDITION的節點
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;//加入到尾節點
    return node;
}

fullyRelease方法this

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)//若是釋放失敗,狀態就變成CANCELLED,而不是CONDITION,因此會在addConditionWaiter方法中被移除
            node.waitStatus = Node.CANCELLED;
    }
}

isOnSyncQueue方法spa

final boolean isOnSyncQueue(Node node) {
    if (node.waitStatus == Node.CONDITION || node.prev == null)//若是狀態是CONDITION或者前置節點爲空,說明還在Condition隊列裏
        return false;
    if (node.next != null) // If has successor, it must be on queue若是有後續節點了,確定是在阻塞隊列裏
        return true;
    /*
     * node.prev can be non-null, but not yet on queue because
     * the CAS to place it on queue can fail. So we have to
     * traverse from tail to make sure it actually made it.  It
     * will always be near the tail in calls to this method, and
     * unless the CAS failed (which is unlikely), it will be
     * there, so we hardly ever traverse much.
     */
    return findNodeFromTail(node);//從阻塞隊列的尾節點遍歷,若是找到當前node,說明在阻塞隊列裏返回true
}

checkInterruptWhileWaiting方法,喚醒前已經中斷,返回THROW_IE,喚醒後中斷,返回REINTERRUPT,沒有中斷,返回0code

private int checkInterruptWhileWaiting(Node node) {
    return Thread.interrupted() ?
        (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
        0;
}
final boolean transferAfterCancelledWait(Node node) {
    //若是cas操做成功,且預期值是CONDITION狀態,說明在喚醒前就中斷了,並加入阻塞隊列
    if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
        enq(node);
        return true;
    }
    
    while (!isOnSyncQueue(node))//一直到阻塞隊列裏,在喚醒後才中斷
        Thread.yield();
    return false;
}

signal方法

喚醒Condition隊列的節點對象

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;//help gc
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
    /*
     * 若是cas操做沒成功,說明已經取消了,就繼續下一個節點
     */
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    /*
     * 沒有取消,就加入阻塞隊列,這個方法在aqs講過了
     * 
     */
    Node p = enq(node);
    int ws = p.waitStatus;//前置節點的狀態
    //大於0,說明前置節點已取消,就輪到當前節點,能夠喚醒
    //小於等於哦,把前置節點的狀態設置爲-1,若是失敗了,喚醒
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);//喚醒
    return true;
}
相關文章
相關標籤/搜索