乱弹java并发(六)-- Condition

Condition可以让线程处于条件挂起的状态,它和锁(Lock)配合使用,Lock实例通过newCondition可以生成一个条件对象,Condition.await()让线程处于挂起等待状态,通过在别的线程中调用Condition.signal和Condition.signalAll唤醒处于等待的线程,它们的功能和Object的wait和notify、notifyAll方法功能是一样,wait和同步关键字配合使用,而Condition和Lock配合使用。在上篇博客学学JUC(五)-- AQS中介绍了AQS的实现,Condition也是基于AQS实现的,来看看代码await方法:

        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;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null)
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

首先也会给处于条件等待的线程创建一个CONDITION模式的节点,并且通过nextWaiter(同步锁等待是通过next指针)指针把所有处于条件等待的线程节点维护在一个链表中:

        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)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

调用await的线程首先必须的是持有Lock锁的线程,所以接下来要把当前线程持有的锁释放掉,无论重入多少次全都释放掉,并且记录下重入的次数,如果当前线程没有持有Condition对应的同步锁,那么程序会抛出IllegalMonitorStateException异常:

    final int fullyRelease(Node node) {
        try {
            int savedState = getState();
            if (release(savedState))
                return savedState;
        } catch (RuntimeException ex) {
            node.waitStatus = Node.CANCELLED;
            throw ex;
        }
        // reach here if release fails
        node.waitStatus = Node.CANCELLED;
        throw new IllegalMonitorStateException();
    }
接下来循环检查当前结点是否处于同步锁等待队列中(通过next结点维护,调用signal或signalAll时回把条件等待队列中的迁移到锁等待队列,后面代码介绍)。如果不再锁等待队列,挂起线程,阻塞在await方法中的LockSupport.park(this)这行代码。在来看看signal方法,signal方法把条件队列中的第一个结点迁移到同步锁等待队列中,并且唤醒线程。
        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) {
        /*
         * If cannot change waitStatus, the node has been 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 c = p.waitStatus;
        if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
而signalAll方法稍微有点不同,它是把条件队列中的所有节点都迁移到锁等待队列中并且唤醒结点对应的线程:

        private void doSignalAll(Node first) {
            lastWaiter = firstWaiter  = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
        }
线程被唤醒之后回到await方法的LockSupport.park(this)这行代码继续往后执行,while循环中再次检查当前线程结点是否处于同步等待队列中(再次检查以免出现线程假唤醒)。线程唤醒之后进入acquireQueued方法尝试获取锁(还原之前记录的重入次数),如果获取失败线程再次被挂起,acquireQueued这个方法在 学学JUC(五)-- AQS分析过了。以上就是Condition的实现原理。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值