java sleep()和wait()的区别

来源:互联网 发布:石油计量表软件 编辑:程序博客网 时间:2024/05/28 14:56

通常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程 downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。

以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例如:

synchronized(obj) {
    while(!condition) {
        obj.wait();
    }
    obj.doSomething();
}

当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。

在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:

synchronized(obj) {
    condition = true;
    obj.notify();
}

需要注意的概念是:

# 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {} 代码段内。

# 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {} 代码段内唤醒A。

# 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。

# 如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。

# obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。

# 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。

 

前面讲了wait/notify机制,Thread还有一个sleep()静态方法,它也能使线程暂停一段时间。sleep与wait的不同点是: sleep并不释放锁,并且sleep的暂停和wait暂停是不一样的。obj.wait会使线程进入obj对象的等待集合中并等待唤醒。

但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。

如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在 wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。

需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException。


举三个例子进行说明:

第一个例子:

/** *  */package com.b510.test;/** * java中的sleep()和wait()的区别 * @author Hongten * @date 2013-12-10 */public class TestD {    public static void main(String[] args) {        new Thread(new Thread1()).start();        try {            Thread.sleep(5000);        } catch (Exception e) {            e.printStackTrace();        }        new Thread(new Thread2()).start();    }        private static class Thread1 implements Runnable{        @Override        public void run(){            synchronized (TestD.class) {            System.out.println("enter thread1...");                System.out.println("thread1 is waiting...");            try {                //调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池                TestD.class.wait();            } catch (Exception e) {                e.printStackTrace();            }            System.out.println("thread1 is going on ....");            System.out.println("thread1 is over!!!");            }        }    }        private static class Thread2 implements Runnable{        @Override        public void run(){            synchronized (TestD.class) {                System.out.println("enter thread2....");                System.out.println("thread2 is sleep....");                //只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。                TestD.class.notify();                //==================                //区别                //如果我们把代码:TestD.class.notify();给注释掉,即TestD.class调用了wait()方法,但是没有调用notify()                //方法,则线程永远处于挂起状态。                try {                    //sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,                    //但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。                    //在调用sleep()方法的过程中,线程不会释放对象锁。                    Thread.sleep(5000);                } catch (Exception e) {                    e.printStackTrace();                }                System.out.println("thread2 is going on....");                System.out.println("thread2 is over!!!");            }        }    }}
运行效果:

enter thread1...thread1 is waiting...enter thread2....thread2 is sleep....thread2 is going on....thread2 is over!!!thread1 is going on ....thread1 is over!!!
如果注释掉代码:

TestD.class.notify();
运行效果:

enter thread1...thread1 is waiting...enter thread2....thread2 is sleep....thread2 is going on....thread2 is over!!!
且程序一直处于挂起状态。


第二个例子:

/** * Thread sleep和wait区别 * @author DreamSea  * 2012-1-15 */public class ThreadTest implements Runnable {    int number = 10;    public void firstMethod() throws Exception {        synchronized (this) {            number += 100;            System.out.println(number);        }    }    public void secondMethod() throws Exception {        synchronized (this) {            /**             * (休息2S,阻塞线程)             * 以验证当前线程对象的机锁被占用时,             * 是否被可以访问其他同步代码块             */            Thread.sleep(2000);            //this.wait(2000);            number *= 200;        }    }    @Override    public void run() {        try {            firstMethod();        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String[] args) throws Exception {        ThreadTest threadTest = new ThreadTest();        Thread thread = new Thread(threadTest);        thread.start();        threadTest.secondMethod();    }}

使用Sleep()方法输出结果:



使用Wait()方法输出结果:



我们来大致分析一下此段代码,main()方法中实例化ThreadTest并启动该线程,然后调用该线程的一个方法(secondMethod()),因为在主线程中调用方法,所以调用的普通方法secondMethod())会先被执行(但并不是普通方法执行完毕该对象的线程方法才执行,普通方法执行过程中,该线程的方法也会被执行,他们是交替执行的,只是在主线程的普通方法会先被执行而已),所以程序运行时会先执行secondMethod(),而secondMethod()方法代码片段中有synchronized block,因此secondMethod方法被执行后,该方法会占有该对象机锁导致该对象的线程方法一直处于阻塞状态,不能执行,直到secondeMethod释放锁;
使用Thread.sleep(2000)方法时,因为sleep在阻塞线程的同时,并持有该对象锁,所以该对象的其他同步线程(secondMethod())无法执行,直到synchronized block执行完毕(sleep休眠完毕),secondMethod()方法才可以执行,因此输出结果为number*200+100;
使用this.wait(2000)方法时,secondMethod()方法被执行后也锁定了该对象的机锁,执行到this.wait(2000)时,该方法会休眠2S并释当前持有的锁,此时该线程的同步方法会被执行(因为secondMethod持有的锁,已经被wait()所释放),因此输出的结果为:number+100;



sleep()和wait()方法的区别已经讲解完毕,若对线程有兴趣的童鞋我在诺诺的问问:在main方法中最后行加入“System.out.println("number="+threadTest.number);”猜猜会输出什么结果。


第三个例子:

class ThreadA {public static void main(String[] args) {ThreadB b = new ThreadB();b.start();System.out.println("b is start....");synchronized (b)// 括号里的b是什么意思,起什么作用?{try {System.out.println("Waiting for b to complete...");b.wait();// 这一句是什么意思,究竟让谁wait?System.out.println("Completed.Now back to main thread");} catch (InterruptedException e) {}}System.out.println("Total is :" + b.total);}}class ThreadB extends Thread {int total;public void run() {synchronized (this) {System.out.println("ThreadB is running..");for (int i = 0; i < 100; i++) {total += i;System.out.println("total is " + total);}notify();}}}
synchronized(b){...};的意思是定义一个同步块,使用b作为资源锁。b.wait();的意思是临时释放锁,并阻塞当前线程,好让其他使用同一把锁的线程有机会执行,在这里要用同一把锁的就是b线程本身.这个线程在执行到一定地方后用notify()通知wait的线程,锁已经用完,待notify()所在的同步块运行完之后,wait所在的线程就可以继续执行.


转载自:

http://blog.csdn.net/tquding/article/details/8650888

http://www.cnblogs.com/hongten/p/hongten_java_sleep_wait.html

http://www.cnblogs.com/DreamSea/archive/2012/01/16/SleepAndWaitDifferent.html

http://blog.csdn.net/clam_clam/article/details/6803667

参考:

Java中sleep()与wait()的区别 :http://jeff-tang.blog.163.com/blog/static/141686909201022010522906/

JAVA—sleep()和wait()的区别:http://blog.csdn.net/clam_clam/article/details/6803667

总结:

线程的调度 :http://uule.iteye.com/blog/1106710
线程调度器按线程的优先级高低选择高优先级线程(进入运行中状态)执行,同时线程调度是抢先式调度,即如果在当前线程执行过程中,一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行。

抢先式调度又分为:时间片方式和独占方式。在时间片方式下,当前活动线程执行完当前时间片后,如果有其他处于就绪状态的相同优先级的线程,系统会将执行权交给其他就绪态的同优先级线程;当前活动线程转入等待执行队列,等待下一个时间片的调度。
在独占方式下,当前活动线程一旦获得执行权,将一直执行下去,直到执行完毕或由于某种原因主动放弃CPU,或者是有一高优先级的线程处于就绪状态。

0 0
原创粉丝点击