wait() 一定需要notify()唤醒吗?

来源:互联网 发布:anjular.js 编辑:程序博客网 时间:2024/04/30 20:50
wait() 一定需要notify()唤醒吗?
20120810

       写这篇文章是源于论坛里的一个帖子,   "线程同步问题,分析下程序运行结果"  其地址为:

      http://topic.csdn.net/u/20120808/17/f1742060-7501-430b-b509-a492eef535f5.html. 

      当时按照一般的wait(),notify()思想做了解释,但楼主质疑,认为解释的与实际情况不一致,再后来测试时,也确实发现楼主的质疑是对的。      虽然楼主已经结贴了,但我觉得还是应该弄清楚,为什么会有这样两种结果。

      先来看一下帖子的代码:

package test;public class ThreadA{public static void main(String[] args){ThreadB b = new ThreadB();Threadc c = new Threadc();c.setName("c线程");b.setName("b线程");c.start();System.out.println(Thread.currentThread().getName()+"is start....");synchronized(c){try{System.out.println("waiting for b1 to complete....");c.wait();System.out.println("Completed.now back to"+Thread.currentThread().getName());b.start();}catch(InterruptedException e){}}}}class ThreadB extends Thread{int total;public void run(){synchronized(this){System.out.println(Thread.currentThread().getName()+"is running..");for(int i=0;i<10;i++){total +=i;}System.out.println("total is"+total);}}}class Threadc extends Thread{int sum=1;public void run(){synchronized(this){System.out.println(Thread.currentThread().getName()+"is running..");for(int i=1;i<10;i++){sum *=i;}System.out.println("sum is"+sum);notify();}}}



代码运行的结果有2种:
1  程序完全运行完,正常退出。
main is start....
c线程 is running..
sum is 362880
waiting for b1 to complete....
Completed.now back tomain
b线程 is running..
total is 45


2  程序停在那“不动”了,不能正常退出。
main is start....
c线程 is running..
sum is 362880
waiting for b1 to complete....


当时我的解释:


“这两种结果产生的原因是以对象c为同步对象的两段代码块,谁先运行的问题。


第一种情况,主线程优先运行到synchonized(c)标记的同步代码块,这时c线程synchronized(this)标记的代码块就暂时无法运行。主线程运行到c.wait()时,主线程释放同步对象的锁,进入阻塞状态,c线程会由阻塞状态变为运行状态。c线程运行完后,通过其 notify(),使主线程由阻塞状态进入运行状态,正常执行完毕。


第二种情况,c线程优先运行到synchronized(this)标记的代码块,这时主线程因为得不到同步对象锁而阻塞。当c线程运行结束后,释放同步对象锁,这时主线程由阻塞进入运行状态,当运行到c.wait()时,进入阻塞状态,因没有程序运行notify(),所以一直阻塞下去了。

       当经楼主质疑后,经过大量测试,发现上面的解释是不正确的。正象楼主质疑的一样,测试发现每次程序运行都是c线程先进入同步代码段,而主线程的同步代码段是在c线程退出同步代码段后进入的。
       关键问题就在这了:  既然每次都是c线程进入同步代码段,那主线程的c.wait()将一直停在那等才对呢,根本就不能有第一种情况出现!因为c线程的notify()已经运行完了!没有谁再会唤醒这个wait了。
       但问题是有时(而且根据测试是多数时候)程序继续运行,而且正常退出了。
      真是不可思议,与自己以前看到的,学到的知识发生冲突了!
    

      为了进一步证实,程序的wait可以不需要 noyify()唤醒,我干脆在Threadc类里,把notify()注释掉了,编译运行(都是多次运行),发现结果和原来一样:有时正常退出,有时停那不动。


      这时,就能得出结论:c.wait()运行后,不需要c.notify()能够唤醒!
      问题又来了,既然不需要唤醒,那应该是每次都能正常退出啊?为什么有时停那了呢?
      继续测试,在主线程的同步代码段里,在c.wait()前加一句:
    System.out.println("线程c的状态是:"+c.isAlive());

      编译运行后,发现所有正常结束的时候,上述语句输出的都是 true. 而不能正常结束的时候,输出的都是false!
    
      因此说明线程正常结束后,会使以这个线程对象运行的wait()等待,退出等待状态!而如果在运行wait()之前,线程已经结束了,则这个wait就没有程序唤醒了。

      看看thread类的原码,证明了我上述观点!

      原码里的join()方法,实际上就是运行的 wait(). 需要运行join的线程运行join方法,实际上是在此线程上调用了需要加入的线程对象的wait()方法,加入的线程运行完后,自然从wait退出了。


     到此,就得出了我的结论:
    1 线程对象的wait()方法运行后,可以不用其notify()方法退出,会在线程结束后,自动退出。

    2 线程间的等待唤醒机制,最好不要用线程对象做同步锁!


原创粉丝点击