深入理解Condition

Condition是一个接口,它主要是由awiat和singal方法组成,awiat方法是放弃自身锁,进入阻塞状态,等待信号进行唤醒,singal是唤醒线程,让线程去重新竞争锁。它和Object的wait和notify方法是一样的。在queue队列中就用到了condition,弄明白condition的原理,对于queue的认识我们可以更好的理解。

看一下Condition方法

    void await() throws InterruptedException;

    
    void awaitUninterruptibly();
  
     
    long awaitNanos(long nanosTimeout) throws InterruptedException;

 
    boolean awaitUntil(Date deadline) throws InterruptedException;

    
    void signal();

    
    void signalAll();

它的实现类是ConditionObject,存在于AbstractQueuedSynchronizer和AbstractQueuedLongSynchronizer中,我们看一下AbstractQueuedSynchronizer中ConditionObject的实现。

ConditionObject的属性

        //   头节点 
        private transient Node firstWaiter;
        //   尾节点
        private transient Node lastWaiter;

Condition内部维护了一个由Node节点组成的单向链表,包括头节点和尾节点,这个链表的作用是存放等待signal信号的线程,线程被封装为Node节点。

await方法

        public final void await() throws InterruptedException {
            if (Thread.interrupted())  // 线程被中断后直接抛出异常,所以我们在调用这个方法的时候最好使用try...catch
                throw new InterruptedException();
            Node node = addConditionWaiter();   // 将当前线程封装成一个Node节点,加入到链表的尾端
            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) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

看一下await中具体方法的调用

        private Node addConditionWaiter() {
            Node t = lastWaiter;  // 保存链表的尾节点
            // 如果t不是null && t.ws不是CONDITION,调用unlinkCancelledWaiters取消当前节点
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;   // 重新保存尾节点
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION); // 把当前线程的ws设为CONDITION,封装成一个Node
            if (t == null)  // 如果t为null,表示链表为空,就把当前节点设为头节点
                firstWaiter = node;
            else            // t不为空,把t在condition队列上的next节点设为node
                t.nextWaiter = node;  
            lastWaiter = node;   // 改变引用,node设为尾节点
            return node;     // 返回该节点
        }

把当前线程的状态设为CONDITION,封装成一个Node,把它添加到队列的尾部,如果当前队列尾节点不是CONDITION状态调用unlinkCancelledWaiters方法,从链表的头结点开始往后遍历,把状态不是CONDITION的节点全部移除.

再看一下unlinkCancelledWaiters方法

        /**
         *  从头往后遍历,移除队列中ws不为CONDITION的节点
         */
        private void unlinkCancelledWaiters() {
            Node t = firstWaiter; // 把当前队列的头节点赋给t
            Node trail = null;    // 设置一个空节点trail
            while (t != null) {   // 如果t不为null
                Node next = t.nextWaiter;  // 把t在等待队列上的下一个节点保存为next
                if (t.waitStatus != Node.CONDITION) {  // 如果t的ws不是CONDITION
                    t.nextWaiter = null;    // t的nextwaiter设为null
                    if (trail == null)      // trail为null,直接把next设为头节点
                        firstWaiter = next;
                    else                   // trail不为null,把next设为trail的nextwaiter
                        trail.nextWaiter = next;
                    if (next == null)      // next为null,直接把trail设为尾节点
                        lastWaiter = trail;
                }
                else   // 这是t的ws是CONDITION的情况,把t赋给trail
                    trail = t;
                t = next;   // 把next赋给t,继续循环
            }
        }

接着看一下fullRelease方法

    /**
     * 释放当前线程所占有的锁
     * 释放失败直接抛出异常并且把当前node的ws设为CANCELLED
     */
    final int fullyRelease(Node node) {
        boolean failed = true;    // 标志--默认表示释放失败
        try {
            int savedState = getState();  // 获取当前线程的状态
            if (release(savedState)) {    // release返回true,表示释放成功
                failed = false;           // failed设为false,表示释放成功
                return savedState;    // 直接返回当前节点的状态
            } else {     // release返回false,直接抛出异常
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)   // 最后如果释放失败,要把node的ws设为CANCELLED
                node.waitStatus = Node.CANCELLED;
        }
    }

通过getState方法获取当前线程的同步状态,使用release方法去释放当前线程占有的同步状态,释放成功,把释放后的状态返回.关于release方法在AQS框架分析中进行了阐述,这里不在细说.

继续往下走,看一下isOnSyncQueue方法

    /**
     * 这个方法主要是来检测node节点是否已经被转移到同步队列上,如果在同步队列上,直接返回true 
     */
    final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null) // node的ws=CONDITION或者node的prev=null,说明node在
            return false;                                           // condition队列上,直接返回false
        if (node.next != null) // 如果node的next不为空,此时说明node必定在同步队列上,
            return true;       // 因为只有同步队列上的node才有next和prev的引用进行连接,所以直接返回true
        /**
         * node的prev可能不为null,但是它仍不在同步队列,可能正在使用CAS把它 
         * 设为尾节点,所以应该从同步队列的尾节点往前遍历,调用findNodeFromTail方法查找node
         */
        return findNodeFromTail(node);
    }
    /**
     * 从尾节点往前遍历,如果发现node在同步队列上,直接返回true
     */
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }

我们使用isOnSyncQueue进行判定,如果返回true,表示当前线程已经进入同步队列,等待获取同步状态,代码继续往下执行;如果返回false,执行while循环,挂起当前线程,当前线程进入阻塞状态,执行checkInterruptWhileWaiting

        /** 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;

        private int checkInterruptWhileWaiting(Node node) {
            return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
        }

        final boolean transferAfterCancelledWait(Node node) {
           if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
               enq(node);
               return true;
           }
        
           while (!isOnSyncQueue(node))
                Thread.yield();
           return false;
        }

在挂起当前线程的过程中,如果线程未被中断,直接返回0,继续while循环;如果当前线程被中断,利用CAS把当前节点的ws设为0,表示线程中断先于唤醒,线程在得到唤醒之前就被中断,即中断在awiat过程中发生,此时会向外抛出异常,返回true告知调用的方法返回THROW_IE。如果当前线程不能使用CAS设置节点的状态,假设singal先发生,此时CAS会失败,等待唤醒信号直到被移动到同步队列,返回false告知上层方法返回REINTRRUPT。transferCancelledWait方法代表了线程因为中断而被唤醒。

重新梳理一下await方法:await是响应中断的,首先检查一下线程如果被中断,直接抛出异常,接着调用addConditionWaiter方法把当前节点加入到condition的队尾,紧着接调用fullyRelease释放当前线程所占有的锁,然后进行循环判定当前节点是否已经转到到同步队列,在循环过程中,还有调用checkInterruptWhileWaiting方法处理中断,break跳出循环。紧接着执行acquireQueued方法,该方法也在AQS框架中进行了分析,返回值为线程在等待得到同步状态的过程中是否被中断过,如果被中断过并且不需要在中断时抛出异常处理,那就把interruptMode = REINTERRUPT。如果被中断,但是node.nextWaiter != null,说明node还在condition队列中,代码执行到这里,线程必定已经进入同步队列中,再次调用unlinkCancelledWaiters方法清除condition队列上ws不CONDITION的节点。最后判断一下interruptMode的值,调用reportInterruptAfterWait方法进行处理。

        private void reportInterruptAfterWait(int interruptMode)
            throws InterruptedException {
            if (interruptMode == THROW_IE)
                throw new InterruptedException();
            else if (interruptMode == REINTERRUPT)
                selfInterrupt();
        }

如果interruptMode=THROE_IE,说明线程在awiat中发生中断,必须抛出异常,如果interruptMode=REINTRRUPT,可能先得到唤醒信号后被中断,即线程再次获取同步状态后要进行自我中断一次。


awaitNanos

        public final long awaitNanos(long nanosTimeout)
                throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            final long deadline = System.nanoTime() + nanosTimeout;
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                if (nanosTimeout <= 0L) {
                    transferAfterCancelledWait(node);
                    break;
                }
                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 deadline - System.nanoTime();
        }

这个方法和await差不多,只不过加了时间限制,如果nanosTimeout >= spinForTimeoutThreshold,在nanosTimeout时间内挂起当前线程,每次循环都计算是否达到deadline,如果达到deadline,调用transferAfterCancelledWait方法,把当前线程从condition队列转移到同步队列,最后返回唤醒后的剩余等待时间。await(long time, TimeUnit unit)方法和这个方法类似,不过最后的返回值是true或false,记录的是transferAfterCancelledWait的返回值。

awaitUninterruptibly是忽略中断的,如果线程被中断,直接把interrupted=true,最后获取到同步状态后再进行自我中断。

        public final void awaitUninterruptibly() {
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            boolean interrupted = false;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if (Thread.interrupted())
                    interrupted = true;
            }
            if (acquireQueued(node, savedState) || interrupted)
                selfInterrupt();
        }

signal

        public final void signal() {
            if (!isHeldExclusively())   // 判断当前线程是否为独占锁
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;   // 利用doSingal方法唤醒condition队列中的第一个线程
            if (first != null)
                doSignal(first);
        }

singal为唤醒condition队列上的头节点的线程,其中isHeldExclusively方法是需要子类去重写的,如果当前线程不是独占线程,直接抛出异常,接着调用doSingal方法。

        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 (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
                return false;

            Node p = enq(node);
            int ws = p.waitStatus;
            if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
                LockSupport.unpark(node.thread);
            return true;
        }
调用transferForSignal去唤醒first的ws,如果利用CAS不能把node的ws设为0,直接返回false,表示线程放弃等待,如果成功,通过自旋把node加入到同步队列,返回值是node的前继节点p,p的ws>0 或者不能使用CAS把p的ws设为singal,表示p已经被取消,此时直接释放node代表的线程,直接返回true,表示唤醒的线程加入同步队列去竞争同步状态了。signalAll是唤醒condition队列上的所有等待线程,它调用的dosingalAll方法会循环唤醒队列中的头节点,直接队列为空。

虽然这个类就主要实现了await和singal这两个方法,但是涉及的方法很多,设计的知识点比较深,我也是当时看了很久想了很久,所以要坚持下去,毕竟努力就有收获。





猜你喜欢

转载自blog.csdn.net/qq_30572275/article/details/80343775
今日推荐