ConditionObject await single 源码

Condition condition = lock.newCondition();

await()

实现可中断条件等待。
1如果当前线程被中断,则抛出 InterruptedException。
2保存由getState返回的锁状态。
3使用保存的状态作为参数调用release ,如果失败则抛出 IllegalMonitorStateException。
4阻塞直到发出信号或被中断。
5通过以保存的状态作为参数调用专门版本的acquire 。
6如果在步骤 4 中被阻塞时被中断,则抛出 InterruptedException
public final void await() throws InterruptedException {
    `一进来就设置中断标志 如果已经设置了 就抛出异常`
    if (Thread.interrupted())
        throw new InterruptedException();
    创建一个结点并清除垃圾节点 然后 添加到队列的尾部 返回当前创建的结点
    Node node = addConditionWaiter();
    完全释放当前线程所持有的锁 就是unlock的过程 移除结点 唤醒等待队列里面的一个结点 主要针对重入锁 释放锁前的 state 值
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 判断节点是否在同步队列中 此时有可能被single 唤醒 cas可能失败 同步队列从后往前遍历同步队列也减少时间  
    // 当没有在同步队列里面 或者说 被唤醒的时候 则将当前线程阻塞
    while (!isOnSyncQueue(node)) {
        // 因为前面只是释放锁 当前线程还在运行 就需要暂停当前线程  
        // 一直暂停中 ...  直到被唤醒
        LockSupport.park(this);
        // 只有unpark 或者 中断的时候才会执行
        // =0 的情况说明既没有被唤醒 也没有被中断打断, park -1 是 中断返回值, 1 是 single 和 中断导致线程运行 竞争成功 
        // 只有在中断的情况下才会跳出这个循环
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // 重新尝试获取锁 就是 lock的过程 而且是公平锁的调用方法 而且 只能在获取到锁之后响应中断
     1如果一下子获取到锁之后 返回false 往下走
     2或者 获取锁失败又在这里park 住了 ... 直到竞争到锁 返回 true 并且判断 当前的中断模式是不是 抛出异常 
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        删除垃圾节点  也就是当前线程
        unlinkCancelledWaiters();
    if (interruptMode != 0)
    // 再次响应中断 根据模式 抛出异常 或者 这只中断标志位
        reportInterruptAfterWait(interruptMode);
}
添加一个新的 等待节点 等待队列 是 双链表结构 但只用到了 单链表
private Node addConditionWaiter() {
    // 获取最后一个等待结点 
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out. 如果最后一个结点不是等待的结点  清除结点
    if (t != null && t.waitStatus != Node.CONDITION) {
        // 清除垃圾节点
        unlinkCancelledWaiters();
        // 重新赋值 最后一个结点
        t = lastWaiter;
    }
    // 创建一个新的等待结点
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
    如果尾结点== null  说明这个队列是空的 所以 头结点尾结点都是这个新创建的node
        firstWaiter = node;
    else
    设置尾结点 为 新创建的node
        t.nextWaiter = node;
    最后一个节点就是当前创建的结点
    lastWaiter = node;
    返回
    return node;
}
从条件队列中取消链接已取消的等待节点。 '仅在持有锁时调用'。  await 肯定持有锁
当在条件等待期间发生取消时,以及在看到 lastWaiter 已被取消时插入新的服务员时,将调用此方法。 
'需要这种方法来避免在没有信号的情况下垃圾保留'。 因此,即使它可能需要完全遍历,它也仅在没有信号的情况下发生超时或取消时才起作用。 
"它遍历所有节点而不是在特定目标处停止以" "取消所有指向垃圾节点的指针的链接",而无需在取消风暴期间进行多次重新遍历。
private void unlinkCancelledWaiters() {
    // 头结点
    Node t = firstWaiter;
    Node trail = null;
    // 遍历链表
    while (t != null) {
        Node next = t.nextWaiter;
        // 判断 t 是否垃圾结点 
        if (t.waitStatus != Node.CONDITION) {
            是垃圾结点
            断开连接
            t.nextWaiter = null;
            if (trail == null)
            // 将 头结点 设置为 下一个
                firstWaiter = next;
            else
    	     // 删除 t 结点 只用到了单链表 直接删除就可以
                trail.nextWaiter = next;
            if (next == null)
               // 下一个结点 == null  则设置尾结点就是trail
                lastWaiter = trail;
        }
        else
        	不是垃圾结点 头还是t
            trail = t;
        更新t
        t = next;
    }
}
使用当前状态值调用 release; 返回保存状态。 取消节点并在失败时抛出异常。
参数:
node – 此等待的条件节点
返回:
之前的同步状态
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;
    }
}
如果一个节点(始终是最初放置在条件队列中的节点)现在正在等待重新获取同步队列,则返回 true。
参数:
节点——节点
返回:
如果重新获取则为真  
// 判断是否在同步队列里面 因为有可能被唤醒 single
final boolean isOnSyncQueue(Node node) {
    // 当前结点是 等待状态 || 当前结点是头结点 返回false 为什么要判断前结点是否为null  是因为 唤醒过程中要执行两个操作 set prev 和 cas 设置前一个结点的后一个结点是 当前结点 ,cas 可能失败 前一个一定不会 
    // 如果当前结点没有被唤醒 就 return false 
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    // 他都有后结点了 说明他一定在同步队列里面 2333  prev 和 next 针对的都是同步队列
    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.
     node.prev 可以是非空的,但还没有在队列中,因为将它放入队列的 CAS 可能会失败。所以我们必须从尾部遍历以确保它确实做到了。在调用这个方法时它总是靠近尾部,除非 CAS 失败(这不太可能),它会在那里,所以我们几乎不会遍历太多。
     */
    // 判断当前结点是否在同步队列里面 倒着往前是因为cas 可能出现异常 
    return findNodeFromTail(node);
}
如果节点通过从尾部向后搜索在同步队列上,则返回 true。 仅在 isOnSyncQueue 需要时调用。
返回:
如果存在则为真
倒着往上找同步队列是否存在当前结点
private boolean findNodeFromTail(Node node) {
    Node t = tail;
    for (;;) {
        if (t == node)
            return true;
        if (t == null)
            return false;
        t = t.prev;
    }
}
检查是否在等待的时候中断
如果没有设置过中断就直接返回 0 
如果设置了中断 并且和 single竞争成功就返回 THROW_IE = -1
如果和 single 竞争失败就返回 REINTERRUPT = 1
private int checkInterruptWhileWaiting(Node node) {
    // 如果当前线程被打上中断标记 并复位 或者设置为中断
    // 设置中断会打断park() 
    return Thread.interrupted() ?
    // 把他添加到同步队列中 模式由中断设为正常 
        (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0;
}
如有必要,在取消等待后将节点传输到同步队列。 如果线程在发出信号之前被取消,则返回 true。
参数:
节点——节点
返回:
如果在节点收到信号之前取消,则为 true
// cas 成功 添加到同步队列里面了 
该方法的返回值代表当前线程是否在park的时候被中断唤醒,
如果为true表示中断在signal调用之前,signal还未执行,那么这个时候会根据await的语义,在await时遇到中断需要抛出interruptedException,返回true就是告诉checkInterruptWhileWaiting返回THROW_IE(-1)
`能走到这个方法 只能 是  调用中断方法  打断了 park`
final boolean transferAfterCancelledWait(Node node) {
    // 将节点由等待结点 设置为正常模式的结点
    `所以在此cas 失败的唯一可能就是 与 single竞争失败 `
    
    if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
        // 然后添加到同步队列里面
        enq(node);
        `竞争成功 或者 没有竞争 就返回true  然后后面的 返回结果就是 当前线程已经被中断过一次了 下次再中断 就要抛异常了 `
        return true;
    }
    /*
     * If we lost out to a signal(), then we can't proceed
     * until it finishes its enq().  Cancelling during an
     * incomplete transfer is both rare and transient, so just
     * spin.
     如果我们输给了一个 signal(),那么在它完成它的 enq() 之前我们不能继续。在不完整的传输过程中取消既罕见又短暂,因此只需旋转即可
     */
    while (!isOnSyncQueue(node))
        Thread.yield();
    return false;
}
根据模式设置当前线程是中断 还是抛出异常
为什么要区分这两种模式 就是因为 这个代码能执行到这里一定是因为 执行中断 才会走到这里 需要重新设置中断标志 要么中断和 single竞争成功要么竞争失败
private void reportInterruptAfterWait(int interruptMode)
    throws InterruptedException {
    if (interruptMode == THROW_IE)
    等待过程中 请求中断 会把等待队列中的 当前线程添加到同步队列中 并在获取到锁的时候 抛出个异常
        throw new InterruptedException();
    else if (interruptMode == REINTERRUPT)
    重新响应中断 single 赢了的情况
        selfInterrupt();
}

single()

将等待时间最长的线程(如果存在)从此条件的等待队列移动到拥有锁的等待队列。
public final void signal() {
    // 如果是当前线程持有这把锁 则唤醒其他线程  
    // 这是因为只有持有锁的 线程才能唤醒
    if (!isHeldExclusively())
    // 不是当前线程抛出异常
        throw new IllegalMonitorStateException();
        // 获取第一个结点
    Node first = firstWaiter;
    // 如果结点不等于空
    if (first != null)
        doSignal(first);
}
删除并转移节点,直到命中未取消的一或空。 从信号中分离出来部分是为了鼓励编译器内联没有服务员的情况。
参数:
first –(非空)条件队列中的第一个节点
把所有的等待队列中的结点都拿出来 到 同步队列
private void doSignal(Node first) {
    do {
        // 把头结点的下一个给头结点 如果为空的时候
        if ( (firstWaiter = first.nextWaiter) == null)
        // 尾结点设为空
            lastWaiter = null;
            // 头结点的下一个 = null  就是 断开联系了 
        first.nextWaiter = null;
        // 把头结点 唤醒加到同步队列中
        // 只唤醒一个 完了就结束 over
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}
将节点从条件队列转移到同步队列。 如果成功则返回真。
参数:
节点——节点
返回:
如果成功传输则为真(否则节点在信号之前被取消)
final boolean transferForSignal(Node node) {
    /*
     * If cannot change waitStatus, the node has been cancelled.
     * 在这里cas 和 中断 抢 看谁成功
     * 如果失败的话 直接返回false
     */
    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).
     */
     cas 成功的情况 添加到同步队列中 并返回一个p结点
    Node p = enq(node);
    // 前一个结点的状态 > 0 或者 把前一个状态设置成 -1  就是需要唤醒后面的线程
    int ws = p.waitStatus;
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
    	// 当前线程唤醒 时间最久的也就是头结点 
             LockSupport.unpark(node.thread);
    return true;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值