ReentranLock之Condition

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yueloveme/article/details/88570183

给大家推荐个靠谱的公众号程序员探索之路,大家一起加油

目录

Condition

属性

等待condition.await()

addConditionWaiter()

isOnSyncQueue(node)

唤醒condition.signal()

transferForSignal(first)

唤醒之后:

checkInterruptWhileWaiting(node)

扫描二维码关注公众号,回复: 5582994 查看本文章

transferAfterCancelledWait(node)

interruptMode的取值

带有超时时间的await

唤醒所有的方法SignalAll


Condition

Condition提供await,signal,signalAll方法,和线程的wait,notify,notifyAll类似

属性

public class ConditionObject implements Condition, java.io.Serializable {

    private static final long serialVersionUID = 1173984872572414699L;

    //头结点

    private transient Node firstWaiter;

    //尾节点

    private transient Node lastWaiter;

Node和上面的node是同一个,只不过这里没有用其他属性,只是用了

Node nextWaiter;

标记当前Node的下一个节点

所以Condition维护的是一个单向队列,当符合条件时会唤醒队列中的节点,所以叫它条件队列,一个ReentranLock对应的可以有很多个condition,每一个condition都有一个条件队列

等待condition.await()

位置:AbstractQueuedSynchronizer 2032
方法目的: 

方法流程

public final void await() throws InterruptedException {

    if (Thread.interrupted())

        throw new InterruptedException();

    Node node = addConditionWaiter();

    int savedState = fullyRelease(node);

    int interruptMode = 0;

// isOnSyncQueue(node) 判断node节点是不是 在阻塞队列中了

//第一次进入循环时 node肯定不在 所以线程挂起 signal唤醒线程时

//会把节点加入到阻塞队列中  然后唤醒 就会从while循环中退出

//或者线程中断了也会退出循环

    while (!isOnSyncQueue(node)) {

        LockSupport.park(this);

        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

            break;

    }

    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

        interruptMode = REINTERRUPT;

    //signal 将条件队列中 的node转移到阻塞队列中 而且 first.nextWaiter = null  但是如果是signal之前出现中断了 node也是会进入阻塞队列的 但是 node.nextWaiter没有处理 所以下面会处理 取消的节点

    if (node.nextWaiter != null) // clean up if cancelled

         //去除节点 纯链表操作

        unlinkCancelledWaiters();

    //处理中断

    if (interruptMode != 0)

        reportInterruptAfterWait(interruptMode);

}

addConditionWaiter()

位置:AbstractQueuedSynchronizer 1848
方法目的:将节点添加到条件队列中

方法流程:

  1. 判断条件队尾是否是取消状态
  2. 进条件队列

private Node addConditionWaiter() {

    Node t = lastWaiter;

    // If lastWaiter is cancelled, clean out.

// t.waitStatus != Node.CONDITION 因为条件队列的waitStatus初始化就是Node.CONDITION

    if (t != null && t.waitStatus != Node.CONDITION) {

//如果是取消的就检查一遍队列是不是取消的 纯链表操作不分析了

        unlinkCancelledWaiters();

        t = lastWaiter;

    }

    Node node = new Node(Thread.currentThread(), Node.CONDITION);

//进条件队列

    if (t == null)

        firstWaiter = node;

    else

        t.nextWaiter = node;

    lastWaiter = node;

    return node;

}

 

位置:AbstractQueuedSynchronizer 1719
方法目的:完成释放锁(独占锁 后面会介绍共享锁)

方法流程:

  1. 完全释放锁
  2. 如果失败 标记当前节点为取消 release和上面的释放锁是一样的

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;

    }

}

isOnSyncQueue(node)

位置:AbstractQueuedSynchronizer 1631
方法目的:判断节点是否在 阻塞队列中

方法流程:

final boolean isOnSyncQueue(Node node) {

//节点waitStatus 是Node.CONDITION或者 前驱为null 没有进入阻塞队列

    if (node.waitStatus == Node.CONDITION || node.prev == null)

        return false;

//后继为不为null 那么 节点进入阻塞队列了

    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是否在队列中

    return findNodeFromTail(node);

}

到这里线程挂起了 那么我们先看到 signal()唤醒方法

唤醒condition.signal()

位置:AbstractQueuedSynchronizer 1937
方法目的:将等待队列队头加入到阻塞队列中竞争锁

方法流程:

public final void signal() {

//检查获取锁的线程是否是当前线程

    if (!isHeldExclusively())

        throw new IllegalMonitorStateException();

    Node first = firstWaiter;

    if (first != null)

        doSignal(first);

}

 

位置:AbstractQueuedSynchronizer 1870
方法目的:将等待队列队头加入到阻塞队列中

方法流程:

private void doSignal(Node first) {

    do {

        if ( (firstWaiter = first.nextWaiter) == null)

            lastWaiter = null;

        first.nextWaiter = null;

    } while (!transferForSignal(first) &&

             (first = firstWaiter) != null);

}

transferForSignal(first)

位置:AbstractQueuedSynchronizer 1670
方法目的:将node加入到阻塞队列

方法流程:

final boolean transferForSignal(Node node) {

    /*

     * If cannot change waitStatus, the node has been cancelled.

     */

     //上面的解释很清楚 如果 waitStatus 不能从 CONDITIONcas成0 代表该几点已经被取消了

    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的前驱节点

    Node p = enq(node);

int ws = p.waitStatus;

//如果 ws > 0 说明前驱节点 取消了 那么node节点直接唤醒

//如果把前驱节点的waitSatus设置为Signal失败那么 也直接唤醒

//大多数情况下 ws是 <= 0 的 而且 cas 前驱的waitStatus为SIGNAL

//为什么可以直接唤醒 因为 唤醒之后还是会请求锁的 请求不到还是会挂起

//只不过是 在请求不到锁的地方挂起

//这里如果前驱节点是取消的 直接唤醒的好处 万一 前驱节点的前驱是head呢

    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))

        LockSupport.unpark(node.thread);

    return true;

}

唤醒之后:

 

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)) {

//在这里挂起了  唤醒之后发现node已经在阻塞队列中 退出循环

        LockSupport.park(this);

        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

            break;

    }

    //如果 请求锁 时中断 而且 不是抛出中断异常

    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

        interruptMode = REINTERRUPT;

    if (node.nextWaiter != null) // clean up if cancelled

        unlinkCancelledWaiters();

    //处理中断 抛出 还是 设置中断标志位

    if (interruptMode != 0)

        reportInterruptAfterWait(interruptMode);

}

checkInterruptWhileWaiting(node)

位置:AbstractQueuedSynchronizer 2001
方法目的:检查node所在的线程是否中断了

方法流程:

private int checkInterruptWhileWaiting(Node node) {

    return Thread.interrupted() ?

        (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :

        0;

}

transferAfterCancelledWait(node)

位置:AbstractQueuedSynchronizer 1697
方法目的:检测节点是在signal 之前还是之后中断的

方法流程:

无论是在哪里中断的 都会进入到阻塞队列中

final boolean transferAfterCancelledWait(Node node) {

    //如果能cas成功说明是 signal之前中断的 因为signal会把节点的waitStatus设置为0

    if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {

        enq(node);

        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.

     */

    //走到这一步的时候 就是上面的 cas waitStatus失败 那就是signal把waitStatus 设置成了0 这里如果node还没有在阻塞队列中说明是 在自旋加入队列中 这里放弃时间片

    while (!isOnSyncQueue(node))

        Thread.yield();

    return false;

}

 

interruptMode的取值

//从英文解释也是很好理解的REINTERRUPT表示需要重新设置中断状态

THROW_IE表示需要抛出中断异常

interruptMode = 0 表示没有出现中断

/** Mode meaning to reinterrupt on exit from wait */

private static final int REINTERRUPT 1;

/** Mode meaning to throw InterruptedException on exit from wait */

private static final int THROW_IE    = -1;

带有超时时间的await

位置:AbstractQueuedSynchronizer 2147
方法目的:

方法流程:

public final boolean await(long time, TimeUnit unit)

        throws InterruptedException {

    //将传入的时间 转成纳秒

    long nanosTimeout = unit.toNanos(time);

    if (Thread.interrupted())

        throw new InterruptedException();

    //添加到等待队列

    Node node = addConditionWaiter();

    //完全释放锁

    int savedState = fullyRelease(node);

    final long deadline = System.nanoTime() + nanosTimeout;

    //是否超时标志位

    boolean timedout = false;

    int interruptMode = 0;

    while (!isOnSyncQueue(node)) {

        if (nanosTimeout <= 0L) {

            //返回 true 说明是转移到 阻塞队列成功 那也就是没有超时

           //返回false 说明在 signal 唤醒之前没有进入到阻塞队列中 那就是超时嘛

            timedout = transferAfterCancelledWait(node);

            break;

        }

        //等待时间超过 1秒 就挂起 如果不是就自旋

        if (nanosTimeout >= spinForTimeoutThreshold)

            LockSupport.parkNanos(this, nanosTimeout);

        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

            break;

        nanosTimeout = deadline - System.nanoTime();

    }

    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

        interruptMode = REINTERRUPT;

    if (node.nextWaiter != null)

        unlinkCancelledWaiters();

    if (interruptMode != 0)

        reportInterruptAfterWait(interruptMode);

    return !timedout;

}

唤醒所有的方法SignalAll

位置:AbstractQueuedSynchronizer 1952
方法目的:唤醒所有等待节点

方法流程:

public final void signalAll() {

    if (!isHeldExclusively())

        throw new IllegalMonitorStateException();

    Node first = firstWaiter;

    if (first != null)

        doSignalAll(first);

}

//这里和 单个的signal不一样的地方就是 循环 条件队列

private void doSignalAll(Node first) {

    lastWaiter = firstWaiter = null;

    do {

        Node next = first.nextWaiter;

        first.nextWaiter = null;

        transferForSignal(first);

        first = next;

    } while (first != null);

}

猜你喜欢

转载自blog.csdn.net/yueloveme/article/details/88570183
今日推荐