美文网首页multiThread
Condition接口原理与应用

Condition接口原理与应用

作者: SunnyMore | 来源:发表于2018-08-20 09:29 被阅读165次

    一. Condition接口简介

     任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。

     通过对比Object的监视器方法和Condition接口,可以更详细地了解Condition的特性,对比如下:

    对比项 Object Monitor Methods Condition
    前置条件 获取对象的锁 1.调用Lock.lock()获取 2.调用Lock.newCondition()获取Condition对象
    调用方式 直接调用,如:object.wait() 直接调用,如:condition.await()
    等待队列个数 一个 多个
    当前线程释放锁并进入等待状态 支持 支持
    当前线程释放锁并进入等待状态,在等待状态中不响应终端 不支持 支持
    当前线程释放锁并进入超时等待状态 支持 支持
    当前线程释放锁并进入等待状态到将来的某个时间 不支持 支持
    唤醒等待队列中的一个线程 支持 支持
    唤醒等待队列中的全部线程 支持 支持

    1.1 Condition接口与示例

     Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。

     Condition的使用方式比较简单,需要注意在调用方法前获取锁,如下代码所示:

        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        
        public void conditionWait() throws InterruptedException {
            lock.lock();
            try {
                condition.await();
            } finally {
                lock.unlock();
            }
        }
        public void conditionSignal() throws InterruptedException {
            lock.lock();
            try {
                condition.signal();
            } finally {
                lock.unlock();
            }
        }
    

     如示例所示,一般都会将Condition对象作为成员变量。当调用await()方法后,当前线程会释放锁并在此等待,而其他线程调用Condition对象的signal()方法,通知当前线程后,当前线程才从await()方法返回,并且在返回前已经获取了锁。

     Condition定义的(部分)方法以及描述如下表:

    方法名称 描述
    void await() throws InterruptedException; 当线程进入等待状态直到被通知(signal)或中断,当前线程将进入运行状态且从await()方法返回的情况,包括:其他线程调用该Condition的signal()或signalAll()方法,而当前线程被选中唤醒; 1. 其他线程(调用interrupt()方法)中断当前线程; 2. 如果当前等待线程从await()方法返回,那么表明该线程已经获取了Condition对象所对应的锁
    void awaitUninterruptibly(); 当前线程进入等待状态直到被通知,从方法名称上可以看出该方法对中断不敏感
    long awaitNanos(long nanosTimeout) throws InterruptedException; 当前线程进入等待状态直到被通知、中断或者超时。返回值表示剩余的时间,如果在nanosTimeout纳秒之前被唤醒,那么返回值就是(nanosTimeout - 实际耗时)。如果返回值是0或者负数,那么可以认定已经超时了
    boolean awaitUntil(Date deadline) throws InterruptedException; 当前线程进入等待状态直到被通知、中断或者到某个时间。如果没有到指定时间就被通知,方法返回true,否则,表示到了指定时间,返回false
    void signal(); 唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁
    void signalAll(); 唤醒所有等待在Condition上的线程,能够从等待方法返回的线程必须获得与Condition相关联的锁

     获取一个Condition必须通过Lock的newCondition()方法。下面通过一个有界队列的示例来深入了解Condition的使用方式。有界队列是一种特殊的队列,当队列为空时,队列的获取操作将会阻塞获取线程,直到队列中有新增元素,当队列已满时,队列的插入操作将会阻塞插入线程,直到队列出现“空位”,如代码清单5-21所示。

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class BoundedQueue<T> {
        private Object[] items;
        // 添加的下标,删除的下标和数组当前数量
        private int addIndex, removeIndex, count;
        private Lock lock = new ReentrantLock();
        private Condition notEmpty = lock.newCondition();
        private Condition notFull = lock.newCondition();
        public BoundedQueue(int size) {
        items = new Object[size];
        }
        // 添加一个元素,如果数组满,则添加线程进入等待状态,直到有"空位"
        public void add(T t) throws InterruptedException {
            lock.lock();try {
            while (count == items.length)
                notFull.await();
                items[addIndex] = t;
                if (++addIndex == items.length)
                addIndex = 0;
                ++count;
                notEmpty.signal();
            } finally {
                lock.unlock();
            }
        }
        // 由头部删除一个元素,如果数组空,则删除线程进入等待状态,直到有新添加元素
        public T remove() throws InterruptedException {
            lock.lock();
            try {
                while (count == 0)
                    notEmpty.await();
                Object x = items[removeIndex];
                if (++removeIndex == items.length)
                    removeIndex = 0;
                --count;
                notFull.signal();
                return (T) x;
            } finally {
                lock.unlock();
            }
        }
    }
    

     上述示例中,BoundedQueue通过add(T t)方法添加一个元素,通过remove()方法移出一个元素。以添加方法为例。

     首先需要获得锁,目的是确保数组修改的可见性和排他性。当数组数量等于数组长度时,表示数组已满,则调用notFull.await(),当前线程随之释放锁并进入等待状态。如果数组数量不等于数组长度,表示数组未满,则添加元素到数组中,同时通知等待在notEmpty上的线程,数组中已经有新元素可以获取。

     在添加和删除方法中使用while循环而非if判断,目的是防止过早或意外的通知,只有条件符合才能够退出循环。回想之前提到的等待/通知的经典范式,二者是非常类似的。


    二. Condition原理分析

     ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队列(以下称为等待队列),该队列是Condition对象实现等待/通知功能的关键。

     下面将分析Condition的实现,主要包括:等待队列、等待和通知,下面提到的Condition如果不加说明均指的是ConditionObject。

    2.1 等待队列

     等待队列是一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列并进入等待状态。事实上,节点的定义复用了同步器中节点的定义,也就是说,同步队列和等待队列中节点类型都是同步器的静态内部类AbstractQueuedSynchronizer.Node。

     一个Condition包含一个等待队列,Condition拥有首节点(firstWaiter)和尾节点(lastWaiter)。当前线程调用Condition.await()方法,将会以当前线程构造节点,并将节点从尾部加入等待队列,等待队列的基本结构如图所示。

    等待队列基本结构

     如图所示,Condition拥有首尾节点的引用,而新增节点只需要将原有的尾节点nextWaiter指向它,并且更新尾节点即可。上述节点引用更新的过程并没有使用CAS保证,原因在于调用await()方法的线程必定是获取了锁的线程,也就是说该过程是由锁来保证线程安全的。

     在Object的监视器模型上,一个对象拥有一个同步队列和等待队列,而并发包中的Lock(更确切地说是同步器)拥有一个同步队列和多个等待队列,其对应关系如图所示。

    同步队列与等待队列

     如图所示,Condition的实现是同步器的内部类,因此每个Condition实例都能够访问同步器提供的方法,相当于每个Condition都拥有所属同步器的引用。

    2.2 等待

     调用Condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁。

     如果从队列(同步队列和等待队列)的角度看await()方法,当调用await()方法时,相当于同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中。

     Condition的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.signal()方法唤醒,而是对等待线程进行中断,则会抛出InterruptedException。

     如果从队列的角度去看,当前线程加入Condition的等待队列,该过程如图示。

    当前线程加入等待队列

     如图所示,同步队列的首节点并不会直接加入等待队列,而是通过addConditionWaiter()方法把当前线程构造成一个新的节点并将其加入等待队列中。

    2.3 通知

     调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中。
    Condition的signal()方法,如代码如下所示:

    public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
    

     调用该方法的前置条件是当前线程必须获取了锁,可以看到signal()方法进行了isHeldExclusively()检查,也就是当前线程必须是获取了锁的线程。接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport唤醒节点中的线程。

     节点从等待队列移动到同步队列的过程如图所示。

    节点从等待队列移动到同步队列

     通过调用同步器的enq(Node node)方法,等待队列中的头节点线程安全地移动到同步队列。当节点移动到同步队列后,当前线程再使用LockSupport唤醒该节点的线程。

     被唤醒后的线程,将从await()方法中的while循环中退出(isOnSyncQueue(Node node)方法返回true,节点已经在同步队列中),进而调用同步器的acquireQueued()方法加入到获取同步状态的竞争中。

     成功获取同步状态(或者说锁)之后,被唤醒的线程将从先前调用的await()方法返回,此时该线程已经成功地获取了锁。

     Condition的signalAll()方法,相当于对等待队列中的每个节点均执行一次signal()方法,效果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。


    三. Condition在生产者/消费者模式中的应用

    生产者-消费者模式是一个十分经典的多线程并发协作的模式,弄懂生产者-消费者问题能够让我们对并发编程的理解加深。所谓生产者-消费者问题,实际上主要是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库,生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为;而消费者只需要从共享数据区中去获取数据,就不再需要关心生产者的行为。但是,这个共享数据区域中应该具备这样的线程间并发协作的功能:

    1. 如果共享数据区已满的话,阻塞生产者继续生产数据放置入内;
    2. 如果共享数据区为空的话,阻塞消费者继续消费数据;

    3.1 使用Lock的Condition的await/signal的消息通知机制实现生产者和消费者的功能

    实现源码如下:

    package com.sunny.jdk.concurrent.condition.app;
    
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * <Description> 使用Condition的wait和signal实现消费者和生产者相互通知的功能<br>
     *
     * @author Sunny<br>
     * @version 1.0<br>
     * @taskId: <br>
     * @createDate 2018/08/21 13:17 <br>
     * @see com.sunny.jdk.concurrent.condition.app <br>
     */
    public class ProducerConsumerApp {
        private static ReentrantLock lock = new ReentrantLock();
        private static Condition full = lock.newCondition();
        private static Condition empty = lock.newCondition();
    
        public static void main(String[] args) {
            LinkedList linkedList = new LinkedList();
            ExecutorService service = Executors.newFixedThreadPool(15);
            for (int i = 0; i < 5; i++) {
                service.submit(new Producer(linkedList, 8, lock));
            }
            for (int i = 0; i < 10; i++) {
                service.submit(new Consumer(linkedList, lock));
            }
        }
    
        static class Producer implements Runnable {
    
            private List<Integer> list;
            private int maxLength;
            private Lock lock;
    
            public Producer(List list, int maxLength, Lock lock) {
                this.list = list;
                this.maxLength = maxLength;
                this.lock = lock;
            }
    
            @Override
            public void run() {
                while (true) {
                    lock.lock();
                    try {
                        while (list.size() == maxLength) {
                            System.out.println("生产者" + Thread.currentThread().getName()
                                    + "  list以达到最大容量【" + maxLength + "】,进行wait");
                            full.await();
                            System.out.println("生产者" + Thread.currentThread().getName() + "  退出wait");
                        }
                        Random random = new Random();
                        int i = random.nextInt();
                        System.out.println("生产者" + Thread.currentThread().getName() + " 生产数据" + i);
                        list.add(i);
                        empty.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
    
    
        static class Consumer implements Runnable {
    
            private List<Integer> list;
            private Lock lock;
    
            public Consumer(List list, Lock lock) {
                this.list = list;
                this.lock = lock;
            }
    
            @Override
            public void run() {
                while (true) {
                    lock.lock();
                    try {
                        while (list.isEmpty()) {
                            System.out.println("消费者" + Thread.currentThread().getName() + "  list为空,进行wait");
                            empty.await();
                            System.out.println("消费者" + Thread.currentThread().getName() + "  退出wait");
                        }
                        Integer element = list.remove(0);
                        System.out.println("消费者" + Thread.currentThread().getName() + "  消费数据:" + element);
                        full.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
    }
    

    四. Condition接口源码

    /*
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     *
     * Written by Doug Lea with assistance from members of JCP JSR-166
     * Expert Group and released to the public domain, as explained at
     * http://creativecommons.org/publicdomain/zero/1.0/
     */
    
    package java.util.concurrent.locks;
    import java.util.concurrent.TimeUnit;
    import java.util.Date;
    
    /**
     * {@code Condition} factors out the {@code Object} monitor
     * methods ({@link Object#wait() wait}, {@link Object#notify notify}
     * and {@link Object#notifyAll notifyAll}) into distinct objects to
     * give the effect of having multiple wait-sets per object, by
     * combining them with the use of arbitrary {@link Lock} implementations.
     * Where a {@code Lock} replaces the use of {@code synchronized} methods
     * and statements, a {@code Condition} replaces the use of the Object
     * monitor methods.
     *
     * <p>Conditions (also known as <em>condition queues</em> or
     * <em>condition variables</em>) provide a means for one thread to
     * suspend execution (to &quot;wait&quot;) until notified by another
     * thread that some state condition may now be true.  Because access
     * to this shared state information occurs in different threads, it
     * must be protected, so a lock of some form is associated with the
     * condition. The key property that waiting for a condition provides
     * is that it <em>atomically</em> releases the associated lock and
     * suspends the current thread, just like {@code Object.wait}.
     *
     * <p>A {@code Condition} instance is intrinsically bound to a lock.
     * To obtain a {@code Condition} instance for a particular {@link Lock}
     * instance use its {@link Lock#newCondition newCondition()} method.
     *
     * <p>As an example, suppose we have a bounded buffer which supports
     * {@code put} and {@code take} methods.  If a
     * {@code take} is attempted on an empty buffer, then the thread will block
     * until an item becomes available; if a {@code put} is attempted on a
     * full buffer, then the thread will block until a space becomes available.
     * We would like to keep waiting {@code put} threads and {@code take}
     * threads in separate wait-sets so that we can use the optimization of
     * only notifying a single thread at a time when items or spaces become
     * available in the buffer. This can be achieved using two
     * {@link Condition} instances.
     * <pre>
     * class BoundedBuffer {
     *   <b>final Lock lock = new ReentrantLock();</b>
     *   final Condition notFull  = <b>lock.newCondition(); </b>
     *   final Condition notEmpty = <b>lock.newCondition(); </b>
     *
     *   final Object[] items = new Object[100];
     *   int putptr, takeptr, count;
     *
     *   public void put(Object x) throws InterruptedException {
     *     <b>lock.lock();
     *     try {</b>
     *       while (count == items.length)
     *         <b>notFull.await();</b>
     *       items[putptr] = x;
     *       if (++putptr == items.length) putptr = 0;
     *       ++count;
     *       <b>notEmpty.signal();</b>
     *     <b>} finally {
     *       lock.unlock();
     *     }</b>
     *   }
     *
     *   public Object take() throws InterruptedException {
     *     <b>lock.lock();
     *     try {</b>
     *       while (count == 0)
     *         <b>notEmpty.await();</b>
     *       Object x = items[takeptr];
     *       if (++takeptr == items.length) takeptr = 0;
     *       --count;
     *       <b>notFull.signal();</b>
     *       return x;
     *     <b>} finally {
     *       lock.unlock();
     *     }</b>
     *   }
     * }
     * </pre>
     *
     * (The {@link java.util.concurrent.ArrayBlockingQueue} class provides
     * this functionality, so there is no reason to implement this
     * sample usage class.)
     *
     * <p>A {@code Condition} implementation can provide behavior and semantics
     * that is
     * different from that of the {@code Object} monitor methods, such as
     * guaranteed ordering for notifications, or not requiring a lock to be held
     * when performing notifications.
     * If an implementation provides such specialized semantics then the
     * implementation must document those semantics.
     *
     * <p>Note that {@code Condition} instances are just normal objects and can
     * themselves be used as the target in a {@code synchronized} statement,
     * and can have their own monitor {@link Object#wait wait} and
     * {@link Object#notify notification} methods invoked.
     * Acquiring the monitor lock of a {@code Condition} instance, or using its
     * monitor methods, has no specified relationship with acquiring the
     * {@link Lock} associated with that {@code Condition} or the use of its
     * {@linkplain #await waiting} and {@linkplain #signal signalling} methods.
     * It is recommended that to avoid confusion you never use {@code Condition}
     * instances in this way, except perhaps within their own implementation.
     *
     * <p>Except where noted, passing a {@code null} value for any parameter
     * will result in a {@link NullPointerException} being thrown.
     *
     * <h3>Implementation Considerations</h3>
     *
     * <p>When waiting upon a {@code Condition}, a &quot;<em>spurious
     * wakeup</em>&quot; is permitted to occur, in
     * general, as a concession to the underlying platform semantics.
     * This has little practical impact on most application programs as a
     * {@code Condition} should always be waited upon in a loop, testing
     * the state predicate that is being waited for.  An implementation is
     * free to remove the possibility of spurious wakeups but it is
     * recommended that applications programmers always assume that they can
     * occur and so always wait in a loop.
     *
     * <p>The three forms of condition waiting
     * (interruptible, non-interruptible, and timed) may differ in their ease of
     * implementation on some platforms and in their performance characteristics.
     * In particular, it may be difficult to provide these features and maintain
     * specific semantics such as ordering guarantees.
     * Further, the ability to interrupt the actual suspension of the thread may
     * not always be feasible to implement on all platforms.
     *
     * <p>Consequently, an implementation is not required to define exactly the
     * same guarantees or semantics for all three forms of waiting, nor is it
     * required to support interruption of the actual suspension of the thread.
     *
     * <p>An implementation is required to
     * clearly document the semantics and guarantees provided by each of the
     * waiting methods, and when an implementation does support interruption of
     * thread suspension then it must obey the interruption semantics as defined
     * in this interface.
     *
     * <p>As interruption generally implies cancellation, and checks for
     * interruption are often infrequent, an implementation can favor responding
     * to an interrupt over normal method return. This is true even if it can be
     * shown that the interrupt occurred after another action that may have
     * unblocked the thread. An implementation should document this behavior.
     *
     * @since 1.5
     * @author Doug Lea
     */
    public interface Condition {
    
        /**
         * Causes the current thread to wait until it is signalled or
         * {@linkplain Thread#interrupt interrupted}.
         *
         * <p>The lock associated with this {@code Condition} is atomically
         * released and the current thread becomes disabled for thread scheduling
         * purposes and lies dormant until <em>one</em> of four things happens:
         * <ul>
         * <li>Some other thread invokes the {@link #signal} method for this
         * {@code Condition} and the current thread happens to be chosen as the
         * thread to be awakened; or
         * <li>Some other thread invokes the {@link #signalAll} method for this
         * {@code Condition}; or
         * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
         * current thread, and interruption of thread suspension is supported; or
         * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
         * </ul>
         *
         * <p>In all cases, before this method can return the current thread must
         * re-acquire the lock associated with this condition. When the
         * thread returns it is <em>guaranteed</em> to hold this lock.
         *
         * <p>If the current thread:
         * <ul>
         * <li>has its interrupted status set on entry to this method; or
         * <li>is {@linkplain Thread#interrupt interrupted} while waiting
         * and interruption of thread suspension is supported,
         * </ul>
         * then {@link InterruptedException} is thrown and the current thread's
         * interrupted status is cleared. It is not specified, in the first
         * case, whether or not the test for interruption occurs before the lock
         * is released.
         *
         * <p><b>Implementation Considerations</b>
         *
         * <p>The current thread is assumed to hold the lock associated with this
         * {@code Condition} when this method is called.
         * It is up to the implementation to determine if this is
         * the case and if not, how to respond. Typically, an exception will be
         * thrown (such as {@link IllegalMonitorStateException}) and the
         * implementation must document that fact.
         *
         * <p>An implementation can favor responding to an interrupt over normal
         * method return in response to a signal. In that case the implementation
         * must ensure that the signal is redirected to another waiting thread, if
         * there is one.
         *
         * @throws InterruptedException if the current thread is interrupted
         *         (and interruption of thread suspension is supported)
         */
        void await() throws InterruptedException;
    
        /**
         * Causes the current thread to wait until it is signalled.
         *
         * <p>The lock associated with this condition is atomically
         * released and the current thread becomes disabled for thread scheduling
         * purposes and lies dormant until <em>one</em> of three things happens:
         * <ul>
         * <li>Some other thread invokes the {@link #signal} method for this
         * {@code Condition} and the current thread happens to be chosen as the
         * thread to be awakened; or
         * <li>Some other thread invokes the {@link #signalAll} method for this
         * {@code Condition}; or
         * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
         * </ul>
         *
         * <p>In all cases, before this method can return the current thread must
         * re-acquire the lock associated with this condition. When the
         * thread returns it is <em>guaranteed</em> to hold this lock.
         *
         * <p>If the current thread's interrupted status is set when it enters
         * this method, or it is {@linkplain Thread#interrupt interrupted}
         * while waiting, it will continue to wait until signalled. When it finally
         * returns from this method its interrupted status will still
         * be set.
         *
         * <p><b>Implementation Considerations</b>
         *
         * <p>The current thread is assumed to hold the lock associated with this
         * {@code Condition} when this method is called.
         * It is up to the implementation to determine if this is
         * the case and if not, how to respond. Typically, an exception will be
         * thrown (such as {@link IllegalMonitorStateException}) and the
         * implementation must document that fact.
         */
        void awaitUninterruptibly();
    
        /**
         * Causes the current thread to wait until it is signalled or interrupted,
         * or the specified waiting time elapses.
         *
         * <p>The lock associated with this condition is atomically
         * released and the current thread becomes disabled for thread scheduling
         * purposes and lies dormant until <em>one</em> of five things happens:
         * <ul>
         * <li>Some other thread invokes the {@link #signal} method for this
         * {@code Condition} and the current thread happens to be chosen as the
         * thread to be awakened; or
         * <li>Some other thread invokes the {@link #signalAll} method for this
         * {@code Condition}; or
         * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
         * current thread, and interruption of thread suspension is supported; or
         * <li>The specified waiting time elapses; or
         * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
         * </ul>
         *
         * <p>In all cases, before this method can return the current thread must
         * re-acquire the lock associated with this condition. When the
         * thread returns it is <em>guaranteed</em> to hold this lock.
         *
         * <p>If the current thread:
         * <ul>
         * <li>has its interrupted status set on entry to this method; or
         * <li>is {@linkplain Thread#interrupt interrupted} while waiting
         * and interruption of thread suspension is supported,
         * </ul>
         * then {@link InterruptedException} is thrown and the current thread's
         * interrupted status is cleared. It is not specified, in the first
         * case, whether or not the test for interruption occurs before the lock
         * is released.
         *
         * <p>The method returns an estimate of the number of nanoseconds
         * remaining to wait given the supplied {@code nanosTimeout}
         * value upon return, or a value less than or equal to zero if it
         * timed out. This value can be used to determine whether and how
         * long to re-wait in cases where the wait returns but an awaited
         * condition still does not hold. Typical uses of this method take
         * the following form:
         *
         *  <pre> {@code
         * boolean aMethod(long timeout, TimeUnit unit) {
         *   long nanos = unit.toNanos(timeout);
         *   lock.lock();
         *   try {
         *     while (!conditionBeingWaitedFor()) {
         *       if (nanos <= 0L)
         *         return false;
         *       nanos = theCondition.awaitNanos(nanos);
         *     }
         *     // ...
         *   } finally {
         *     lock.unlock();
         *   }
         * }}</pre>
         *
         * <p>Design note: This method requires a nanosecond argument so
         * as to avoid truncation errors in reporting remaining times.
         * Such precision loss would make it difficult for programmers to
         * ensure that total waiting times are not systematically shorter
         * than specified when re-waits occur.
         *
         * <p><b>Implementation Considerations</b>
         *
         * <p>The current thread is assumed to hold the lock associated with this
         * {@code Condition} when this method is called.
         * It is up to the implementation to determine if this is
         * the case and if not, how to respond. Typically, an exception will be
         * thrown (such as {@link IllegalMonitorStateException}) and the
         * implementation must document that fact.
         *
         * <p>An implementation can favor responding to an interrupt over normal
         * method return in response to a signal, or over indicating the elapse
         * of the specified waiting time. In either case the implementation
         * must ensure that the signal is redirected to another waiting thread, if
         * there is one.
         *
         * @param nanosTimeout the maximum time to wait, in nanoseconds
         * @return an estimate of the {@code nanosTimeout} value minus
         *         the time spent waiting upon return from this method.
         *         A positive value may be used as the argument to a
         *         subsequent call to this method to finish waiting out
         *         the desired time.  A value less than or equal to zero
         *         indicates that no time remains.
         * @throws InterruptedException if the current thread is interrupted
         *         (and interruption of thread suspension is supported)
         */
        long awaitNanos(long nanosTimeout) throws InterruptedException;
    
        /**
         * Causes the current thread to wait until it is signalled or interrupted,
         * or the specified waiting time elapses. This method is behaviorally
         * equivalent to:
         *  <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre>
         *
         * @param time the maximum time to wait
         * @param unit the time unit of the {@code time} argument
         * @return {@code false} if the waiting time detectably elapsed
         *         before return from the method, else {@code true}
         * @throws InterruptedException if the current thread is interrupted
         *         (and interruption of thread suspension is supported)
         */
        boolean await(long time, TimeUnit unit) throws InterruptedException;
    
        /**
         * Causes the current thread to wait until it is signalled or interrupted,
         * or the specified deadline elapses.
         *
         * <p>The lock associated with this condition is atomically
         * released and the current thread becomes disabled for thread scheduling
         * purposes and lies dormant until <em>one</em> of five things happens:
         * <ul>
         * <li>Some other thread invokes the {@link #signal} method for this
         * {@code Condition} and the current thread happens to be chosen as the
         * thread to be awakened; or
         * <li>Some other thread invokes the {@link #signalAll} method for this
         * {@code Condition}; or
         * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
         * current thread, and interruption of thread suspension is supported; or
         * <li>The specified deadline elapses; or
         * <li>A &quot;<em>spurious wakeup</em>&quot; occurs.
         * </ul>
         *
         * <p>In all cases, before this method can return the current thread must
         * re-acquire the lock associated with this condition. When the
         * thread returns it is <em>guaranteed</em> to hold this lock.
         *
         *
         * <p>If the current thread:
         * <ul>
         * <li>has its interrupted status set on entry to this method; or
         * <li>is {@linkplain Thread#interrupt interrupted} while waiting
         * and interruption of thread suspension is supported,
         * </ul>
         * then {@link InterruptedException} is thrown and the current thread's
         * interrupted status is cleared. It is not specified, in the first
         * case, whether or not the test for interruption occurs before the lock
         * is released.
         *
         *
         * <p>The return value indicates whether the deadline has elapsed,
         * which can be used as follows:
         *  <pre> {@code
         * boolean aMethod(Date deadline) {
         *   boolean stillWaiting = true;
         *   lock.lock();
         *   try {
         *     while (!conditionBeingWaitedFor()) {
         *       if (!stillWaiting)
         *         return false;
         *       stillWaiting = theCondition.awaitUntil(deadline);
         *     }
         *     // ...
         *   } finally {
         *     lock.unlock();
         *   }
         * }}</pre>
         *
         * <p><b>Implementation Considerations</b>
         *
         * <p>The current thread is assumed to hold the lock associated with this
         * {@code Condition} when this method is called.
         * It is up to the implementation to determine if this is
         * the case and if not, how to respond. Typically, an exception will be
         * thrown (such as {@link IllegalMonitorStateException}) and the
         * implementation must document that fact.
         *
         * <p>An implementation can favor responding to an interrupt over normal
         * method return in response to a signal, or over indicating the passing
         * of the specified deadline. In either case the implementation
         * must ensure that the signal is redirected to another waiting thread, if
         * there is one.
         *
         * @param deadline the absolute time to wait until
         * @return {@code false} if the deadline has elapsed upon return, else
         *         {@code true}
         * @throws InterruptedException if the current thread is interrupted
         *         (and interruption of thread suspension is supported)
         */
        boolean awaitUntil(Date deadline) throws InterruptedException;
    
        /**
         * Wakes up one waiting thread.
         *
         * <p>If any threads are waiting on this condition then one
         * is selected for waking up. That thread must then re-acquire the
         * lock before returning from {@code await}.
         *
         * <p><b>Implementation Considerations</b>
         *
         * <p>An implementation may (and typically does) require that the
         * current thread hold the lock associated with this {@code
         * Condition} when this method is called. Implementations must
         * document this precondition and any actions taken if the lock is
         * not held. Typically, an exception such as {@link
         * IllegalMonitorStateException} will be thrown.
         */
        void signal();
    
        /**
         * Wakes up all waiting threads.
         *
         * <p>If any threads are waiting on this condition then they are
         * all woken up. Each thread must re-acquire the lock before it can
         * return from {@code await}.
         *
         * <p><b>Implementation Considerations</b>
         *
         * <p>An implementation may (and typically does) require that the
         * current thread hold the lock associated with this {@code
         * Condition} when this method is called. Implementations must
         * document this precondition and any actions taken if the lock is
         * not held. Typically, an exception such as {@link
         * IllegalMonitorStateException} will be thrown.
         */
        void signalAll();
    }
    
    

    参考《Java并发编程的艺术》

    相关文章

      网友评论

        本文标题:Condition接口原理与应用

        本文链接:https://www.haomeiwen.com/subject/zcrfiftx.html