欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中線程的等待與喚醒_動力節(jié)點Java學(xué)院整理

 更新時間:2017年05月26日 15:30:47   投稿:mrr  
在Object.java中,定義了wait(), notify()和notifyAll()等接口。wait()的作用是讓當(dāng)前線程進(jìn)入等待狀態(tài),同時,wait()也會讓當(dāng)前線程釋放它所持有的鎖。下面通過本文給大家介紹Java中線程的等待與喚醒知識,感興趣的朋友一起看看吧

wait(), notify(), notifyAll()等方法介紹

在Object.java中,定義了wait(), notify()和notifyAll()等接口。wait()的作用是讓當(dāng)前線程進(jìn)入等待狀態(tài),同時,wait()也會讓當(dāng)前線程釋放它所持有的鎖。而notify()和notifyAll()的作用,則是喚醒當(dāng)前對象上的等待線程;notify()是喚醒單個線程,而notifyAll()是喚醒所有的線程。

Object類中關(guān)于等待/喚醒的API詳細(xì)信息如下:

notify()        -- 喚醒在此對象監(jiān)視器上等待的單個線程。
notifyAll()   -- 喚醒在此對象監(jiān)視器上等待的所有線程。
wait()                                         -- 讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。
wait(long timeout)                    -- 讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。
wait(long timeout, int nanos)  -- 讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當(dāng)前線程,或者已超過某個實際時間量”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。 

 wait()和notify()示例

下面通過示例演示"wait()和notify()配合使用的情形"。

// WaitTest.java的源碼
class ThreadA extends Thread{
  public ThreadA(String name) {
    super(name);
  }
  public void run() {
    synchronized (this) {
      System.out.println(Thread.currentThread().getName()+" call notify()");
      // 喚醒當(dāng)前的wait線程
      notify();
    }
  }
}
public class WaitTest {
  public static void main(String[] args) {
    ThreadA t1 = new ThreadA("t1");
    synchronized(t1) {
      try {
        // 啟動“線程t1”
        System.out.println(Thread.currentThread().getName()+" start t1");
        t1.start();
        // 主線程等待t1通過notify()喚醒。
        System.out.println(Thread.currentThread().getName()+" wait()");
        t1.wait();
        System.out.println(Thread.currentThread().getName()+" continue");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

運行結(jié)果:

main start t1
main wait()
t1 call notify()
main continue

結(jié)果說明:

如下圖,說明了“主線程”和“線程t1”的流程。

(01) 注意,圖中"主線程" 代表“主線程main”。"線程t1" 代表WaitTest中啟動的“線程t1”。 而“鎖” 代表“t1這個對象的同步鎖”。

(02) “主線程”通過 new ThreadA("t1") 新建“線程t1”。隨后通過synchronized(t1)獲取“t1對象的同步鎖”。然后調(diào)用t1.start()啟動“線程t1”。

(03) “主線程”執(zhí)行t1.wait() 釋放“t1對象的鎖”并且進(jìn)入“等待(阻塞)狀態(tài)”。等待t1對象上的線程通過notify() 或 notifyAll()將其喚醒。

(04) “線程t1”運行之后,通過synchronized(this)獲取“當(dāng)前對象的鎖”;接著調(diào)用notify()喚醒“當(dāng)前對象上的等待線程”,也就是喚醒“主線程”。

(05) “線程t1”運行完畢之后,釋放“當(dāng)前對象的鎖”。緊接著,“主線程”獲取“t1對象的鎖”,然后接著運行。

對于上面的代碼?曾經(jīng)有個朋友問到過:t1.wait()應(yīng)該是讓“線程t1”等待;但是,為什么卻是讓“主線程main”等待了呢?

在解答該問題前,我們先看看jdk文檔中關(guān)于wait的一段介紹:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
In other words, this method behaves exactly as if it simply performs the call wait(0).
The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

注意:jdk的解釋中,說wait()的作用是讓“當(dāng)前線程”等待,而“當(dāng)前線程”是指正在cpu上運行的線程!

這也意味著,雖然t1.wait()是通過“線程t1”調(diào)用的wait()方法,但是調(diào)用t1.wait()的地方是在“主線程main”中。而主線程必須是“當(dāng)前線程”,也就是運行狀態(tài),才可以執(zhí)行t1.wait()。所以,此時的“當(dāng)前線程”是“主線程main”!因此,t1.wait()是讓“主線程”等待,而不是“線程t1”! 

wait(long timeout)和notify()

wait(long timeout)會讓當(dāng)前線程處于“等待(阻塞)狀態(tài)”,“直到其他線程調(diào)用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量”,當(dāng)前線程被喚醒(進(jìn)入“就緒狀態(tài)”)。

下面的示例就是演示wait(long timeout)在超時情況下,線程被喚醒的情況。  

// WaitTimeoutTest.java的源碼
class ThreadA extends Thread{
  public ThreadA(String name) {
    super(name);
  }
  public void run() {
    System.out.println(Thread.currentThread().getName() + " run ");
    // 死循環(huán),不斷運行。
    while(true)
      ;
  }
}
public class WaitTimeoutTest {
  public static void main(String[] args) {
    ThreadA t1 = new ThreadA("t1");
    synchronized(t1) {
      try {
        // 啟動“線程t1”
        System.out.println(Thread.currentThread().getName() + " start t1");
        t1.start();
        // 主線程等待t1通過notify()喚醒 或 notifyAll()喚醒,或超過3000ms延時;然后才被喚醒。
        System.out.println(Thread.currentThread().getName() + " call wait ");
        t1.wait(3000);
        System.out.println(Thread.currentThread().getName() + " continue");
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

運行結(jié)果:

main start t1
main call wait 
t1 run         // 大約3秒之后...輸出“main continue”
main continue

結(jié)果說明:

如下圖,說明了“主線程”和“線程t1”的流程。

(01) 注意,圖中"主線程" 代表WaitTimeoutTest主線程(即,線程main)。"線程t1" 代表WaitTest中啟動的線程t1。 而“鎖” 代表“t1這個對象的同步鎖”。

(02) 主線程main執(zhí)行t1.start()啟動“線程t1”。

(03) 主線程main執(zhí)行t1.wait(3000),此時,主線程進(jìn)入“阻塞狀態(tài)”。需要“用于t1對象鎖的線程通過notify() 或者 notifyAll()將其喚醒” 或者 “超時3000ms之后”,主線程main才進(jìn)入到“就緒狀態(tài)”,然后才可以運行。

(04) “線程t1”運行之后,進(jìn)入了死循環(huán),一直不斷的運行。

(05) 超時3000ms之后,主線程main會進(jìn)入到“就緒狀態(tài)”,然后接著進(jìn)入“運行狀態(tài)”。 

wait() 和 notifyAll()

通過前面的示例,我們知道 notify() 可以喚醒在此對象監(jiān)視器上等待的單個線程。

下面,我們通過示例演示notifyAll()的用法;它的作用是喚醒在此對象監(jiān)視器上等待的所有線程。 

public class NotifyAllTest {
   private static Object obj = new Object();
   public static void main(String[] args) {
     ThreadA t1 = new ThreadA("t1");
     ThreadA t2 = new ThreadA("t2");
     ThreadA t3 = new ThreadA("t3");
     t1.start();
     t2.start();
     t3.start();
     try {
       System.out.println(Thread.currentThread().getName()+" sleep(3000)");
       Thread.sleep(3000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     synchronized(obj) {
       // 主線程等待喚醒。
       System.out.println(Thread.currentThread().getName()+" notifyAll()");
       obj.notifyAll();
     }
   }
   static class ThreadA extends Thread{
     public ThreadA(String name){
       super(name);
     }
     public void run() {
       synchronized (obj) {
         try {
           // 打印輸出結(jié)果
           System.out.println(Thread.currentThread().getName() + " wait");
           // 喚醒當(dāng)前的wait線程
           obj.wait();
           // 打印輸出結(jié)果
           System.out.println(Thread.currentThread().getName() + " continue");
         } catch (InterruptedException e) {
           e.printStackTrace();
         }
       }
     }
   }
 }

運行結(jié)果: 

t1 wait
main sleep(3000)
t3 wait
t2 wait
main notifyAll()
t2 continue
t3 continue
t1 continue 

結(jié)果說明:

參考下面的流程圖。

(01) 主線程中新建并且啟動了3個線程"t1", "t2"和"t3"。

(02) 主線程通過sleep(3000)休眠3秒。在主線程休眠3秒的過程中,我們假設(shè)"t1", "t2"和"t3"這3個線程都運行了。以"t1"為例,當(dāng)它運行的時候,它會執(zhí)行obj.wait()等待其它線程通過notify()或額nofityAll()來喚醒它;相同的道理,"t2"和"t3"也會等待其它線程通過nofity()或nofityAll()來喚醒它們。

(03) 主線程休眠3秒之后,接著運行。執(zhí)行 obj.notifyAll() 喚醒obj上的等待線程,即喚醒"t1", "t2"和"t3"這3個線程。 緊接著,主線程的synchronized(obj)運行完畢之后,主線程釋放“obj鎖”。這樣,"t1", "t2"和"t3"就可以獲取“obj鎖”而繼續(xù)運行了! 

為什么notify(), wait()等函數(shù)定義在Object中,而不是Thread中

Object中的wait(), notify()等函數(shù),和synchronized一樣,會對“對象的同步鎖”進(jìn)行操作。

wait()會使“當(dāng)前線程”等待,因為線程進(jìn)入等待狀態(tài),所以線程應(yīng)該釋放它鎖持有的“同步鎖”,否則其它線程獲取不到該“同步鎖”而無法運行!

OK,線程調(diào)用wait()之后,會釋放它鎖持有的“同步鎖”;而且,根據(jù)前面的介紹,我們知道:等待線程可以被notify()或notifyAll()喚醒?,F(xiàn)在,請思考一個問題:notify()是依據(jù)什么喚醒等待線程的?或者說,wait()等待線程和notify()之間是通過什么關(guān)聯(lián)起來的?答案是:依據(jù)“對象的同步鎖”。

負(fù)責(zé)喚醒等待線程的那個線程(我們稱為“喚醒線程”),它只有在獲取“該對象的同步鎖”(這里的同步鎖必須和等待線程的同步鎖是同一個),并且調(diào)用notify()或notifyAll()方法之后,才能喚醒等待線程。雖然,等待線程被喚醒;但是,它不能立刻執(zhí)行,因為喚醒線程還持有“該對象的同步鎖”。必須等到喚醒線程釋放了“對象的同步鎖”之后,等待線程才能獲取到“對象的同步鎖”進(jìn)而繼續(xù)運行。

總之,notify(), wait()依賴于“同步鎖”,而“同步鎖”是對象鎖持有,并且每個對象有且僅有一個!這就是為什么notify(), wait()等函數(shù)定義在Object類,而不是Thread類中的原因。

以上所述是小編給大家介紹的Java中線程的等待與喚醒,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java 重命名 Excel 工作表并設(shè)置工作表標(biāo)簽顏色的示例代碼

    Java 重命名 Excel 工作表并設(shè)置工作表標(biāo)簽顏色的示例代碼

    這篇文章主要介紹了Java 重命名 Excel 工作表并設(shè)置工作表標(biāo)簽顏色的示例代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • 詳解使用Spring MVC統(tǒng)一異常處理實戰(zhàn)

    詳解使用Spring MVC統(tǒng)一異常處理實戰(zhàn)

    本篇文章主要介紹了詳解使用Spring MVC統(tǒng)一異常處理實戰(zhàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • 詳解spring cloud feign踩坑記錄

    詳解spring cloud feign踩坑記錄

    這篇文章主要介紹了spring cloud feign踩坑記錄,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • idea激活A(yù)ctivateJrebel熱部署的方法詳解

    idea激活A(yù)ctivateJrebel熱部署的方法詳解

    這篇文章主要介紹了idea激活A(yù)ctivateJrebel熱部署的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • 分享5個Java接口性能提升的通用技巧

    分享5個Java接口性能提升的通用技巧

    作為后端開發(fā)人員,我們總是在編寫各種API。這些API在服務(wù)初期可能表現(xiàn)不錯,但隨著用戶數(shù)量的增長,一開始響應(yīng)很快的API越來越慢,這時候你就需要考慮如何優(yōu)化你的API性能了。在這篇文章中,我總結(jié)了一些行之有效的API性能優(yōu)化技巧,希望能給有需要的朋友一些幫助
    2023-01-01
  • Java實現(xiàn)滑動驗證碼(前端部分)

    Java實現(xiàn)滑動驗證碼(前端部分)

    這篇文章主要為大家介紹了如何用Java語言實現(xiàn)滑動驗證碼的生成(前端部分),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價值,感興趣的小伙伴可以跟隨小編學(xué)習(xí)一下
    2022-10-10
  • 五種Java多線程同步的方法

    五種Java多線程同步的方法

    這篇文章主要為大家詳細(xì)介紹了五種Java多線程同步的方法,需要的朋友可以參考下
    2015-09-09
  • Java高級用法中的JNA類型映射注意細(xì)節(jié)及使用問題

    Java高級用法中的JNA類型映射注意細(xì)節(jié)及使用問題

    本文介紹了在使用JNA方法映射中應(yīng)該注意的一些細(xì)節(jié)和具體的使用問題,對java??JNA類型映射注意細(xì)節(jié)感興趣的朋友一起看看吧
    2022-04-04
  • Java ArrayAdapter用法案例詳解

    Java ArrayAdapter用法案例詳解

    這篇文章主要介紹了Java ArrayAdapter用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java性能優(yōu)化之?dāng)?shù)據(jù)結(jié)構(gòu)實例代碼

    Java性能優(yōu)化之?dāng)?shù)據(jù)結(jié)構(gòu)實例代碼

    這篇文章主要介紹了Java性能優(yōu)化之?dāng)?shù)據(jù)結(jié)構(gòu)實例代碼,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01

最新評論