Condition源码分析

Condition接口

概述

condition与Lock的实现类结合使用。 如果Lock替换了synchronized方法和语句的使用,则Condition将替换Object监视方法(wait,notify和notifyAll)的使用。

condition,也称为condition queue或者condition variables,能让一个线程阻塞在条件变量上,直到其他线程通知该线程条件变量现在可能为true。当一个线程等待条件时,它会自动释放相关联的锁,并陷入阻塞状态,就跟Object.wait()方法一样。

一个Condition实例一个Lock实例绑定,要获取特定Lock实例的Condition实例,可以调用Lock实例的newCondition()方法。

以生产者/消费者模式为例,假设我们有一个有界缓冲区,它支持put和take方法。 如果缓冲区为空,则消费线程将阻塞,直到缓冲区有内容为止; 如果缓冲区已满,则生产线程阻塞,直到缓冲区有空间可用为止。 我们希望把put线程和take线程放在不同的waitset中,以便我们在缓冲区中的内容可用或空间可用时,实现只通知一个线程。 这可以使用两个Condition实例来实现。

 class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0)
         notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   }
 }
 

Condition实现类提供与Object监视器方法不同的行为和语义,例如在waitset中阻塞等待时可以不响应中断,多个waitset。

在等待条件时,虚假唤醒是可以发生的。因此应用程序的程序员应该编写循环,在循环中等待条件。

等待条件有以下三种形式:

1、不可中断:void awaitUninterruptibly();

2、可中断:void await() throws InterruptedException;

3、定时: long awaitNanos(long nanosTimeout) throws InterruptedException;   返回值表示当前剩余的时间。

 

await()方法

await方法使当前线程阻塞等待,直到被通知或者被中断。

与该condition关联的lock会被自动释放,并且由于线程调度的原因线程变得不可用,直到以下情形之一发生:

1、其他线程调用了这个condition的signal()方法,并且当前线程被选为唤醒的线程;
2、其他线程调用了这个condition的signalAll()方法;
3、其他线程调用当前线程的Thread.interrupt()方法;
4、一个虚假唤醒发生时;

以上所有情形中,当前线程都必须重新获得与该condition关联的lock,才能从await方法返回。

如果当前线程在阻塞等待时被中断,将会抛出 InterruptedException,并清除中断状态。

signal()方法

唤醒等待线程。

如果存在线程在条件上等待,选择其中一个作为被唤醒的线程。

在调用该方法时,要求当前线程持有与condition关联的Lock。否则抛出IllegalMonitorStateException。

 

ConditionObject

ConditionObject是Condition在java并发中的具体实现,它是AQS的内部类。因为Condition相关操作都需要获取锁,所以作为AQS的内部类很合理。

等待

await方法

该方法实现了响应中断的条件等待。

1、前置检查,如果线程被中断,抛出中断异常。

2、当前线程封装成节点添加到条件队列。

3、唤醒当前线程的节点(头节点)的后继节点。调用await方法的当前线程必须是持有锁的,当前线程所在的节点只能是持有锁的头节点。所以它在使用state作为参数调用release方法唤醒头节点的后继结点时应该总会成功,如果失败抛出IllegalMonitorStateException。

4、调用park方法阻塞等待直到收到signal信号或者中断信号。

5、线程被唤醒后,从while循环退出,调用acquireQueued方法自旋等待获取锁。

  •  如果是因为signal被唤醒,则节点从条件队列转移到同步队列,不满足while循环条件,退出循环;
  •  如果是因为中断被唤醒,通过break退出循环;

6、如果阻塞过程中发生中断,线程被唤醒后会判断中断状态,并抛出中断异常。

ConditionObject的await方法与object.wait方法的流程比较:

wait方法:进入waitSet  >> 释放锁 >> 阻塞等待条件变量 >> 被唤醒,进入entryList等待获取锁

await方法:进入条件队列 >> 唤醒同步队列的后继节点 >> 阻塞等待条件变量 >> 被唤醒,自旋等待获取锁

  /**
         * Implements interruptible condition wait.
         * 
    *
  1. If current thread is interrupted, throw InterruptedException. *
  2. Save lock state returned by {@link #getState}. *
  3. Invoke {@link #release} with saved state as argument, * throwing IllegalMonitorStateException if it fails. *
  4. Block until signalled or interrupted. *
  5. Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. *
  6. If interrupted while blocked in step 4, throw InterruptedException. *
*/ 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); //调用park方法阻塞等待 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; //如果是因为中断被唤醒,break退出循环 } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) //如果是因为中断而被唤醒 reportInterruptAfterWait(interruptMode); //抛出中断异常 }

addConditionWaiter方法

将当前线程包装成节点,添加到条件队列。

1、如果条件队列中没有节点,设置firstWaiter的引用;如果条件队列中已有节点,设置lastWaiter的nextWaiter引用;

2、修改lastWaiter的引用;

 /**
         * Adds a new waiter to wait queue.
         * @return its new wait node
         */
        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;  //设置条件队列的尾节点的nextWaiter引用
            lastWaiter = node;   //最后修改条件队列的尾节点的引用
            return node;
        }

unlinkCancelledWaiters方法

从条件队列中解除cencelled节点的链接。

  /**
         * Unlinks cancelled waiter nodes from condition queue.
         * Called only while holding lock. This is called when
         * cancellation occurred during condition wait, and upon
         * insertion of a new waiter when lastWaiter is seen to have
         * been cancelled. This method is needed to avoid garbage
         * retention in the absence of signals. So even though it may
         * require a full traversal, it comes into play only when
         * timeouts or cancellations occur in the absence of
         * signals. It traverses all nodes rather than stopping at a
         * particular target to unlink all pointers to garbage nodes
         * without requiring many re-traversals during cancellation
         * storms.
         */
        private void unlinkCancelledWaiters() {
            Node t = firstWaiter;
            Node trail = null;
            while (t != null) {
                Node next = t.nextWaiter;
                if (t.waitStatus != Node.CONDITION) {
                    t.nextWaiter = null;  //删除它的next引用
                    if (trail == null)
                        firstWaiter = next; //让firstWaiter指向它原先的next引用
                    else
                        trail.nextWaiter = next;//让它的前驱的next引用指向它原先的next引用
                    if (next == null)
                        lastWaiter = trail;
                }
                else
                    trail = t; //用trail记录循环过程中节点的有效(waitStatus为condition)前驱节点
                t = next;
            }
        }

 

fullyRelease方法

根据当前状态值调用release方法,返回当前状态值。

/**
     * Invokes release with current state value; returns saved state.
     * Cancels node and throws exception on failure.
     * @param node the condition node for this wait
     * @return previous sync state
     */
    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;
        }
    }

release方法

唤醒同步队列的头节点的后继节点。

 /**
     * Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {@link #tryRelease} returns true.
     * This method can be used to implement method {@link Lock#unlock}.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @return the value returned from {@link #tryRelease}
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); //唤醒头节点的后继节点
            return true;
        }
        return false;
    }

awaitUninterruptibly方法

conditionObject与object的wait/notify方法的一个很大区别就是,实现了在阻塞等待条件变量时,可以不响应中断。

在conditionObject中,判断park方法是从因中断返回,还是因为signal返回有2种方式:

1、调用Thread.interrupted()判断线程的中断状态是否为true。

2、调用isOnSyncQueue(node)判断节点是否在同步队列中,如果在同步队列中,则是因signal返回。

下面会说到,调用conditionObject的signal方法,会将线程所在的节点从条件队列转移到同步队列中。

在该方法实现中,如果是因为中断唤醒,则节点不在同步队列中,仍满足while循环条件,会继续调用park方法阻塞等待。

  /**
         * Implements uninterruptible condition wait.
         * 
    *
  1. Save lock state returned by {@link #getState}. *
  2. Invoke {@link #release} with saved state as argument, * throwing IllegalMonitorStateException if it fails. *
  3. Block until signalled. *
  4. Reacquire by invoking specialized version of * {@link #acquire} with saved state as argument. *
*/ public final void awaitUninterruptibly() { Node node = addConditionWaiter(); int savedState = fullyRelease(node); boolean interrupted = false; while (!isOnSyncQueue(node)) { //如果节点不在同步队列中 LockSupport.park(this); //调用park方法阻塞等待 if (Thread.interrupted()) interrupted = true; } if (acquireQueued(node, savedState) || interrupted) selfInterrupt(); }

 

唤醒

signal方法

1、前置检查,判断当前线程是否是获取了锁的线程,如果不是抛出IllegalMonitorStateException。

2、获取条件队列的头结点,头结点不为空执行doSignal方法。

    /**
         * Moves the longest-waiting thread, if one exists, from the
         * wait queue for this condition to the wait queue for the
         * owning lock.
         *
         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
         *         returns {@code false}
         */
        public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }

doSignal方法

调用transferForSignal方法将条件队列的头节点从条件队列转移到同步队列,并且,将该节点从条件队列删除。

 /**
         * Removes and transfers nodes until hit non-cancelled one or
         * null. Split out from signal in part to encourage compilers
         * to inline the case of no waiters.
         * @param first (non-null) the first node on condition queue
         */
        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

transferForSignal方法

将节点从条件队列转移到同步队列,如果转移成功,返回true。如果在signal该节点之前,该节点已经取消,返回false。

1、使用cas将节点的waitStatus从CONDITION设置为0,如果不成功,说明节点的waitStatus已经是cancelled,返回false。

2、将节点放入同步队列,并获取它在同步队列的前驱节点的waitStatus。

3、如果前驱节点的waitStatus不为cancelled,使用cas将该前驱节点的waitStatus设置为SIGNAL。如果前驱节点的waitStatus为cancelled,或者cas操作执行不成功,调用unpark方法唤醒该节点的线程进行resync。

为什么在当前驱节点的waitStatus为cancelled,或者将waitStatus设置为SIGNAL的cas操作执行不成功时,才调用unpark方法唤醒该节点的线程?调用signal方法不总应该是调用unpark方法唤醒线程吗?前驱节点的waitStatus为cancelled,还唤醒后继节点的线程,是否意味着该前驱节点的waitStatus是错误的?

首先,调用ConditionObject的signal方法,不像Object.notify方法那样,最终总会调用unpark方法唤醒线程。它将节点从条件队列转移到同步队列,将调用unpark方法唤醒线程的操作交给了节点的前驱节点去做。当前驱节点成为头节点后,释放锁时会唤醒该节点。当然,有个前提条件是,节点的前驱节点的waitStatus必须为SIGNAL,才会在释放锁时唤醒后继节点。所以,只有当前驱节点的waitStatus为cancelled,或者将其waitStatus设置为SIGNAL的cas操作执行不成功时,才会在这里就调用unpark方法唤醒线程。

 /**
     * Transfers a node from a condition queue onto sync queue.
     * Returns true if successful.
     * @param node the node
     * @return true if successfully transferred (else the node was
     * cancelled before signal)
     */
    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 ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread); 
        return true;
    }

参考:condition API

你可能感兴趣的:(java高并发实践)