1 wait/sleep 的区别
1.1 sleep源码
//直接分析源码和注释,很快理解 public static native void sleep(long millis) throws InterruptedException; /** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds plus the specified * number of nanoseconds, 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 * * @param nanos * {@code 0-999999} additional nanoseconds to sleep * * @throws IllegalArgumentException * if the value of {@code millis} is negative, or the value of * {@code nanos} is not in the range {@code 0-999999} * * @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 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); } /** * Initializes a Thread with the current AccessControlContext. * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean) */
1.2 waitd的源码
//一个本地方法,两个重载方法 public final native void wait(long timeout) throws InterruptedException; /** * Causes the current thread to wait until another thread invokes the * {@link java.lang.Object#notify()} method or the * {@link java.lang.Object#notifyAll()} method for this object, or * some other thread interrupts the current thread, or a certain * amount of real time has elapsed. * <p> * This method is similar to the {@code wait} method of one * argument, but it allows finer control over the amount of time to * wait for a notification before giving up. The amount of real time, * measured in nanoseconds, is given by: * <blockquote> * <pre> * 1000000*timeout+nanos</pre></blockquote> * <p> * In all other respects, this method does the same thing as the * method {@link #wait(long)} of one argument. In particular, * {@code wait(0, 0)} means the same thing as {@code wait}. * <p> * The current thread must own this object's monitor. The thread * releases ownership of this monitor and waits until either of the * following two conditions has occurred: * <ul> * <li>Another thread notifies threads waiting on this object's monitor * to wake up either through a call to the {@code notify} method * or the {@code notifyAll} method. * <li>The timeout period, specified by {@code timeout} * milliseconds plus {@code nanos} nanoseconds arguments, has * elapsed. * </ul> * <p> * The thread then waits until it can re-obtain ownership of the * monitor and resumes execution. * <p> * As in the one argument version, interrupts and spurious wakeups are * possible, and this method should always be used in a loop: * <pre> * synchronized (obj) { * while (<condition does not hold>) * obj.wait(timeout, nanos); * ... // Perform action appropriate to condition * } * </pre> * 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. * @param nanos additional time, in nanoseconds range * 0-999999. * @throws IllegalArgumentException if the value of timeout is * negative or the value of nanos is * not in the range 0-999999. * @throws IllegalMonitorStateException if the current thread is not * the owner of this 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. */ 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); } public final void wait() throws InterruptedException { wait(0); }
1.3 两者之间的区别
- 1 来自不同的类
- sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep 不会释放对象锁。
- wait 是Object 类的方法,对此对象调用wait 方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify 方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
- 2 关于锁的释放
- wait会释放锁,sleep抱着锁睡觉,不释放锁
- 3 使用的范围不同的
- wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)
- sleep可以在任何地方
- 4 是否需要捕获异常
- sleep必须要捕获异常。sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。但在sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。
- wait也需要捕获异常。wait属于Object的成员方法,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll()方法唤醒该进程;如果线程拥有某个或某些对象的同步锁,那么在调用了wait()后,这个线程就会释放它持有的所有同步资源,而不限于这个被调用了wait()方法的对象。wait()方法也同样会在wait的过程中有可能被其他对象调用interrupt()方法而产生
企业当中一般使用:java.util.concurrent.TimeUnit.DAYS/SECONDS/...中进行睡眠
1.4 一些补充性知识点:
- sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。
- sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。sleep(100L)是占用cpu,线程休眠100毫秒,其他进程不能再占用cpu资源。
- wait(100L)是进入等待池中等待,交出cpu等系统资源供其他进程使用,在这100毫秒中,该线程可以被其他线程notify,但不同的是其他在等待池中的线程不被notify不会出来,但这个线程在等待100毫秒后会自动进入就绪队列等待系统分配资源。
- 换句话说,sleep(100)在100毫秒后肯定会运行,但wait在100毫秒后还有等待os调用分配资源,所以wait100的停止运行时间是不确定的,但至少是100毫秒。就是说sleep有时间限制的就像闹钟一样到时候就叫了,而wait是无限期的除非用户主动notify。
- Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
1.5 wait、notify和notifyAll解释
- 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。wait:线程自动释放其占有的对象锁,并等待notify。
- 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。notify:唤醒一个正在wait当前对象锁的线程,并让它拿到对象锁。
- 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。notifyAll:唤醒所有正在wait前对象锁的线程。
1.6 notify和notifyAll区别
- notify只是唤醒一个正在wait当前对象锁的线程,而notifyAll唤醒所有。
- notify是本地方法,具体唤醒哪一个线程由虚拟机控制;notifyAll后并不是所有的线程都能马上往下执行,它们只是跳出了wait状态,接下来它们还会是竞争对象锁。
注意事项:
- 任何一个时刻,对象的控制权(monitor)只能被一个线程拥有。
- 无论是执行对象的wait、notify还是notifyAll方法,必须保证当前运行的线程取得了该对象的控制权(monitor)
- 如果在没有控制权的线程里执行对象的以上三种方法,就会报java.lang.IllegalMonitorStateException异常。
1.7 线程取得控制权的方法有三
- 执行对象的某个同步实例方法。
- 执行对象对应类的同步静态方法。
- 执行对该对象加同步锁的同步块。
总结表格
~ | wait | sleep |
---|---|---|
同步 | 只能在同步上下文中调用wait方法,否则或抛出IIIegalMonitorStateException异常 | 任意地方 |
作用对象 | wait方法定义在Object类中,作用于对象本身 | sleep方法定义在Java.lang.Thread中,作用于当前线程 |
释放锁资源 | 会释放 | 不会释放 |
唤醒条件 | 其他线程调用对象的notify()或者notifyAll()方法 | 超时或者调用interrupt()方法 |
方法属性 | wait是实例方法 | sleep是Thread类的静态方法 |