wait() 一定需要notify()喚醒嗎?

來源:互聯網
上載者:User
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 線程間的等待喚醒機制,最好不要用線程對象做同步鎖!


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.