JDK8源码阅读:wait方法和sleep方法的区别

waitsleep的区别,这真是在面试中一个高高高高频的面试题。

哈哈,要想说完这两个家伙的所有区别还是不太容易的,今天我们就来总结下这两个方法的区别。


把小本本拿出来,一条条记下来。

我们先来看看源码,是不是一听要看源码就脑阔疼,放心,这两个方法源码还是容易理解的。因为它们两个是native方法😂。

    /**
     * Causes the current thread to wait until either another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object, or a
     * specified amount of time has elapsed.
     * <p>
     * The current thread must own this object's monitor.
     * <p>
     * This method causes the current thread (call it <var>T</var>) to
     * place itself in the wait set for this object and then to relinquish
     * any and all synchronization claims on this object. Thread <var>T</var>
     * becomes disabled for thread scheduling purposes and lies dormant
     * until one of four things happens:
     * <ul>
     * <li>Some other thread invokes the {@code notify} method for this
     * object and thread <var>T</var> happens to be arbitrarily chosen as
     * the thread to be awakened.
     * <li>Some other thread invokes the {@code notifyAll} method for this
     * object.
     * <li>Some other thread {@linkplain Thread#interrupt() interrupts}
     * thread <var>T</var>.
     * <li>The specified amount of real time has elapsed, more or less.  If
     * {@code timeout} is zero, however, then real time is not taken into
     * consideration and the thread simply waits until notified.
     * </ul>
     * The thread <var>T</var> is then removed from the wait set for this
     * object and re-enabled for thread scheduling. It then competes in the
     * usual manner with other threads for the right to synchronize on the
     * object; once it has gained control of the object, all its
     * synchronization claims on the object are restored to the status quo
     * ante - that is, to the situation as of the time that the {@code wait}
     * method was invoked. Thread <var>T</var> then returns from the
     * invocation of the {@code wait} method. Thus, on return from the
     * {@code wait} method, the synchronization state of the object and of
     * thread {@code T} is exactly as it was when the {@code wait} method
     * was invoked.
     * <p>
     * A thread can also wake up without being notified, interrupted, or
     * timing out, a so-called <i>spurious wakeup</i>.  While this will rarely
     * occur in practice, applications must guard against it by testing for
     * the condition that should have caused the thread to be awakened, and
     * continuing to wait if the condition is not satisfied.  In other words,
     * waits should always occur in loops, like this one:
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait(timeout);
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * (For more information on this topic, see Section 3.2.3 in Doug Lea's
     * "Concurrent Programming in Java (Second Edition)" (Addison-Wesley,
     * 2000), or Item 50 in Joshua Bloch's "Effective Java Programming
     * Language Guide" (Addison-Wesley, 2001).
     *
     * <p>If the current thread is {@linkplain java.lang.Thread#interrupt()
     * interrupted} by any thread before or while it is waiting, then an
     * {@code InterruptedException} is thrown.  This exception is not
     * thrown until the lock status of this object has been restored as
     * described above.
     *
     * <p>
     * Note that the {@code wait} method, as it places the current thread
     * into the wait set for this object, unlocks only this object; any
     * other objects on which the current thread may be synchronized remain
     * locked while the thread waits.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @param      timeout   the maximum time to wait in milliseconds.
     * @throws  IllegalArgumentException      if the value of timeout is
     *               negative.
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @throws  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait() throws InterruptedException {
        wait(0);
    }
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static native void sleep(long millis) throws InterruptedException;
    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }

你肯定在想,源码这么简单为什么要把注释也展示出来。
因为是native方法,我们只能靠看注释了解它们的一些信息了。

我们先来看看sleep方法的注释

* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.

还是比较简单的,主要说了该方法会导致当前正在执行的线程休眠,是临时的暂停执行哦。
这个睡眠时间是我们必须要传的,休眠时间的精度受系统的定时器和调度器影响。
最后一点比较重要,不会放弃拥有的锁

我们再来看看wait方法的注释

是不是觉得wait方法的注释好长呀,我第一次看的时候也觉得😅。
所以咱们就拆开来,一点一点啃掉这根硬骨头吧。(嗯?怎么说的物种都变了喃

* Causes the current thread to wait until either another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object, or a
* specified amount of time has elapsed.

首先前几行注释告诉我们,该方法会导致当前线程进入wait的状态。
然后只能被其它线程调用Object的notify()或notifyAll()方法唤醒,也可以指定了被唤醒的时间,这样也可以自动被唤醒。如果传的时间为0的话,就真的只能被其它线程唤醒了。(唉,命运就掌握在别的线程手里了

* The current thread must own this object's monitor.
* <p>
* This method causes the current thread (call it <var>T</var>) to
* place itself in the wait set for this object and then to relinquish
* any and all synchronization claims on this object. 

敲黑板,重点来啦。
如果要调用wait方法是有条件的,当前线程必须要拥有该对象的监视器。
你可能会问,这是什么意思呀?
别着急,看下面的代码我们瞬间就懂了。

String lock = "a";
synchronized (lock) {
    lock.wait();
}

其实就是,当前线程要拥有lock对象的锁时,才能调用lock对象的wait方法。顺便提一下,notify和notifyAll方法也需要先拥有对象的锁才能调用。
不然会抛出java.lang.IllegalMonitorStateException异常。

好了,我们现在可以来总结下啦

区别1:sleep方法是属于java.lang.Thread类;wait方法是属于java.lang.Object类。
区别2:sleep方法是静态方法;wait方法不是静态方法,是一个final方法,不能被子类重写。
区别3:sleep方法不用获得锁就能执行;wait方法必须要获得对象的锁时,才能调用该对象的wait方法。
区别4:sleep方法必须要传参数,参数为0时,表示休眠0毫秒,所以会马上被唤醒;wait方法传的参数为0时,表示永久休眠,只能被其它线程调用notify和notifyAll方法唤醒。
区别5:sleep方法不会放弃获取到的锁;wait方法会放弃获取到的锁(其实也能想的明白,你不放弃,其它线程怎么获取到该对象的锁来唤醒当前线程哇)。

它们两个还是有共同点的

比如都会抛出编译时异常InterruptedException,该异常会在调用那个线程的interrupt()方法时被抛出。

举个栗子🌰,现在我们有A和B两个线程,如果A线程执行了sleep或者wait方法进入休眠状态,此时B线程调用A线程对象.interrupt()方法时,A线程就会抛出InterruptedException异常,相当于也被唤醒了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容