java基础-线程状态分析-ReentrantLock源码分析2

在上篇中,已经分析简单分析了lock和unlock的原理,本篇中,主要分析Condition原理。

  1. await方法
public final void await() throws InterruptedException {
//线程被中断,直接抛出中断异常
    if (Thread.interrupted())
        throw new InterruptedException();
        //向条件队列中添加一个
    Node node = addConditionWaiter();
    //记录await前重入了几次
    int savedState = fullyRelease(node);
    //**********这一步骤后,其他线程就可以进程lock了
    int interruptMode = 0;
    //节点不在同步队列中,挂起当前线程
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    //在同步队列中,就需要重新抢锁
    //*******此处可能会有疑惑,我都在condition队列中了,怎么还会在同步队列中????
    //这里需要结合signal代码来看
    
    //抢锁的时候,还是原来的重入次数
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
复制代码

单独看上上面的代码确实是一脸疑惑,怎么还会在同步队列中,下面先看下signal做了啥

public final void signal() {
    if (!isHeldExclusively())
    //非持锁线程,抛出异常
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}
复制代码

doSignal会将条件队列的Node转移到同步队列,

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) {
    /*
     * If cannot change waitStatus, the node has been cancelled.
     */
     //只有节点处于cancelled状态才会返回false,在非中断的各种模式中,不会出现cancelled状态的
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    /*
     * Splice onto queue and try to set waitStatus of predecessor to
     * indicate that thread is (probably) waiting. If cancelled or
     * attempt to set waitStatus fails, wake up to resync (in which
     * case the waitStatus can be transiently and harmlessly wrong).
     */
    //将节点添加到同步队列,并返回前驱节点
    Node p = enq(node);
    int ws = p.waitStatus;
    //因为这两步不是原子操作,所以下面cas失败是有可能的,失败的情况下,
    
    //此线程一定被设定为SIGNAL状态,可以看lock源码分析
    //执行LockSuupport.park,那么unpark给个令牌,无论park是先执行还是后执行都不会堵塞
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}
复制代码

那下面接着await,判定节点是否在同步队列中,

final boolean isOnSyncQueue(Node node) {
//当Node状态由CONDITION变为0,且还未执行enq的时候,node.dev就是null,说明还没到同步队列
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
        //有不为0状态的后驱,一定是在同步队列中
    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.
     */
     //下面描述的enq方法的cas可能会失败
     //非原子的操作,很多种肯能,此时另一个线程正在lock,将其节点添加到tail后,
     //那么当前从条件队列到同步队列的Node就是node.prev!=null node.next=null
     //下面的方法从头到尾遍历
    return findNodeFromTail(node);
}
复制代码

enq方法

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}
复制代码

最简单流程分析:

  1. T1,T2,T3三个线程抢锁,同步队列如下

image.png 2. T1线程进行condition.await

此时T1会释放锁,由于没有新来的线程竞争,那么下一个获取锁就是T2

image.png

  1. T2拿到锁后,执行condition.signal()

T1从条件队列到同步队列,等待唤醒(T1已经park挂起了,必须T2执行完unlock后,后续激活才会执行T1的unpark)

image.png

猜你喜欢

转载自juejin.im/post/7041213782474883079
今日推荐