0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

圖解Java多線程中的wait()和notify()方法

Android編程精選 ? 來源:CSDN-Killing Vibe ? 2023-03-22 09:29 ? 次閱讀

一、線程間等待與喚醒機制

wait()和notify()是Object類的方法,用于線程的等待與喚醒,必須搭配synchronized 鎖來使用。

多線程并發(fā)的場景下,有時需要某些線程先執(zhí)行,這些線程執(zhí)行結(jié)束后其他線程再繼續(xù)執(zhí)行。

比如: 一個長跑比賽,裁判員要等跑步運動員沖線了才能宣判比賽結(jié)束,那裁判員線程就得等待所有的運動員線程運行結(jié)束后,再喚醒這個裁判線程。

二、等待方法wait()

wait 做的事情:

使當(dāng)前執(zhí)行代碼的線程進行等待. (把線程放到等待隊列中)

釋放當(dāng)前的鎖

滿足一定條件時被喚醒, 重新嘗試獲取這個鎖.

wait 要搭配 synchronized 來使用. 脫離 synchronized 使用 wait 會直接拋出異常.

887454d4-c7dc-11ed-bfe3-dac502259ad0.png

wait 結(jié)束等待的條件:

其他線程調(diào)用該對象的 notify 方法.

wait 等待時間超時 (wait 方法提供一個帶有 timeout 參數(shù)的版本, 來指定等待時間).

其他線程調(diào)用該等待線程的 interrupted 方法, 導(dǎo)致 wait 拋出 InterruptedException 異常.

注意事項:

調(diào)用wait()方法的前提是首先要獲取該對象的鎖(synchronize對象鎖)

調(diào)用wait()方法會釋放鎖,本線程進入等待隊列等待被喚醒,被喚醒后不是立即恢復(fù)執(zhí)行,而是進入阻塞隊列,競爭鎖

等待方法:

1.癡漢方法,死等,線程進入阻塞態(tài)(WAITING)直到有其他線程調(diào)用notify方法喚醒

88996dc8-c7dc-11ed-bfe3-dac502259ad0.png

2.等待一段時間,若在該時間內(nèi)線程被喚醒,則繼續(xù)執(zhí)行,若超過相應(yīng)時間還沒有其他線程喚醒此線程,此線程不再等待,恢復(fù)執(zhí)行。

88ab614a-c7dc-11ed-bfe3-dac502259ad0.png

調(diào)用wait方法之后:

88d99f06-c7dc-11ed-bfe3-dac502259ad0.png

三、喚醒方法notify()

notify 方法是喚醒等待的線程.

方法notify()也要在同步方法或同步塊中調(diào)用,該方法是用來通知那些可能等待該對象的對象鎖的其它線程,對其發(fā)出通知notify,并使它們重新獲取該對象的對象鎖。

如果有多個線程等待,則有線程調(diào)度器隨機挑選出一個呈 wait 狀態(tài)的線程。(并沒有 “先來后到”)

在notify()方法后,當(dāng)前線程不會馬上釋放該對象鎖,要等到執(zhí)行notify()方法的線程將程序執(zhí)行完,也就是退出同步代碼塊之后才會釋放對象鎖。

注意事項:

notify():隨機喚醒一個處在等待狀態(tài)的線程。

notifyAll():喚醒所有處在等待狀態(tài)的線程。

無論是wait還是notify方法,都需要搭配synchronized鎖來使用(等待和喚醒,也是需要對象)

89317672-c7dc-11ed-bfe3-dac502259ad0.png

四、關(guān)于wait和notify內(nèi)部等待問題(重要)

對于wait和notify方法,其實有一個阻塞隊列也有一個等待隊列。

阻塞隊列表示同一時間只有一個線程能獲取到鎖,其他線程進入阻塞隊列

等待隊列表示調(diào)用wait (首先此線程要獲取到鎖,進入等待隊列,釋放鎖)

舉個栗子:

現(xiàn)有如下定義的等待線程任務(wù)

privatestaticclassWaitTaskimplementsRunnable{
privateObjectlock;
publicWaitTask(Objectlock){
this.lock=lock;
}
@Override
publicvoidrun(){
synchronized(lock){
System.out.println(Thread.currentThread().getName()+"準(zhǔn)備進入等待狀態(tài)");
//此線程在等待lock對象的notify方法喚醒
try{
lock.wait();
Thread.sleep(1000);
}catch(InterruptedExceptione){
thrownewRuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"等待結(jié)束,本線程繼續(xù)執(zhí)行");
}
}
}

然后創(chuàng)建三個等待線程:

8961dd4e-c7dc-11ed-bfe3-dac502259ad0.png

由于同一時間只有一個線程(隨機調(diào)度)能獲取到synchronized鎖,所以會有兩個線程沒競爭到鎖,從而進入了阻塞隊列。

這里假如t2先競爭到了鎖,所以先會阻塞t1和t3:

89af03bc-c7dc-11ed-bfe3-dac502259ad0.png

又由于調(diào)用wait方法會釋放鎖,調(diào)用wait方法的線程t2就會進入等待隊列,直到被notify喚醒或者超時自動喚醒。

89c6f49a-c7dc-11ed-bfe3-dac502259ad0.png

然后此時lock對象已經(jīng)被釋放了,所以t1和t3 又可以去競爭這個鎖了,就從阻塞隊列里面競爭鎖。

這里假如t3 競爭到了鎖,阻塞隊列只剩下t1:

89da9c7a-c7dc-11ed-bfe3-dac502259ad0.png

然后t3運行到了wait方法,釋放鎖,然后進入等待隊列:

89f51a96-c7dc-11ed-bfe3-dac502259ad0.png

然后重復(fù)這些操作~~,最后t1,t2,t3 都進入了等待隊列中,等待notify線程喚醒(這里假設(shè)notify要放在這些線程start后的好幾秒后,因為notify線程也是和這些線程并發(fā)執(zhí)行的,所以等待隊列中的線程隨時可能被喚醒)

8a103be6-c7dc-11ed-bfe3-dac502259ad0.png

重點來了:

在等待隊列中的線程,被notify喚醒之后,會直接回到阻塞隊列去競爭鎖?。?!而不是直接喚醒~

舉個栗子:

拿notifyAll()來舉例,假如此時等待隊列中有三個線程t1,t2,t3,那么調(diào)用notifyAll()會直接把它們?nèi)齻€直接從等待隊列中進入到阻塞隊列中:

8a28df34-c7dc-11ed-bfe3-dac502259ad0.png

然后再去競爭這個鎖,去執(zhí)行wait之后的代碼~~

五、完整代碼(僅供測試用)

privatestaticclassWaitTaskimplementsRunnable{
privateObjectlock;
publicWaitTask(Objectlock){
this.lock=lock;
}
@Override
publicvoidrun(){
synchronized(lock){
System.out.println(Thread.currentThread().getName()+"準(zhǔn)備進入等待狀態(tài)");
//此線程在等待lock對象的notify方法喚醒
try{
lock.wait();
Thread.sleep(1000);
}catch(InterruptedExceptione){
thrownewRuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"等待結(jié)束,本線程繼續(xù)執(zhí)行");
}
}
}
privatestaticclassNotifyTaskimplementsRunnable{
privateObjectlock;
publicNotifyTask(Objectlock){
this.lock=lock;
}
@Override
publicvoidrun(){
synchronized(lock){
System.out.println("準(zhǔn)備喚醒");
//喚醒所有線程(隨機)
lock.notifyAll();
System.out.println("喚醒結(jié)束");
}
}
}

publicstaticvoidmain(String[]args)throwsInterruptedException{
Objectlock=newObject();
Objectlock2=newObject();
//創(chuàng)建三個等待線程
Threadt1=newThread(newWaitTask(lock),"t1");
Threadt2=newThread(newWaitTask(lock),"t2");
Threadt3=newThread(newWaitTask(lock),"t3");
//創(chuàng)建一個喚醒線程
Threadnotify=newThread(newNotifyTask(lock2),"notify線程");
t1.start();
t2.start();
t3.start();
;
Thread.sleep(100);
notify.start();
//當(dāng)前正在執(zhí)行的線程數(shù)
Thread.sleep(2000);
System.out.println(Thread.activeCount()-1);
}

六、wait和sleep方法的區(qū)別(面試題):

wait方法是Object類提供的方法,需要搭配synchronized鎖來使用,調(diào)用wait方法會釋放鎖,線程進入WAITING狀態(tài),等待被其他線程喚醒或者超時自動喚醒,喚醒之后的線程需要再次競爭synchronized鎖才能繼續(xù)執(zhí)行。

sleep方法是Thread類提供的方法,調(diào)用sleep方法的線程進入TIMED_WAITING狀態(tài),不會釋放鎖,時間到自動喚醒。

總結(jié)

以上就是多線程場景下wait和notify方法的詳解和注意事項了,碼字不易,有幫助的話別忘了關(guān)注,點贊+收藏哦~

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2946

    瀏覽量

    104368
  • 多線程
    +關(guān)注

    關(guān)注

    0

    文章

    277

    瀏覽量

    19878
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4695

    瀏覽量

    68081
  • 線程
    +關(guān)注

    關(guān)注

    0

    文章

    502

    瀏覽量

    19613
  • WAIT
    +關(guān)注

    關(guān)注

    0

    文章

    4

    瀏覽量

    2503

原文標(biāo)題:圖解 Java多線程中的 wait() 和 notify() 方法

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Java多線程的用法

    本文將介紹一下Java多線程的用法。 基礎(chǔ)介紹 什么是多線程 指的是在一個進程同時運行多個線程,每個
    的頭像 發(fā)表于 09-30 17:07 ?894次閱讀

    Javasleep和wait的區(qū)別

    sleep()、suspend()、resume()方法不推薦使用,推薦使用wait()、notify()、notifyAll()?! leep()方法是使
    發(fā)表于 11-25 11:50

    Java線程阻塞方法大全

    ()方法,當(dāng)前線程轉(zhuǎn)A入阻塞狀態(tài),直到線程B運行結(jié)束,線程A才由阻塞狀態(tài)轉(zhuǎn)為可執(zhí)行狀態(tài)。以上是Java
    發(fā)表于 04-02 15:42

    Java線程喚醒與阻塞規(guī)則

    的join()方法,當(dāng)前線程轉(zhuǎn)A入阻塞狀態(tài),直到線程B運行結(jié)束,線程A才由阻塞狀態(tài)轉(zhuǎn)為可執(zhí)行狀態(tài)。以上是Java
    發(fā)表于 07-06 15:11

    java】兩種方式實現(xiàn)線程通信:三個線程交替打印AABBCC

    ()、signal()方法。使用wait/notify進行線程通信只能夠隨機喚醒,增加了上下文的切換時間,使用await/signal可以實現(xiàn)精準(zhǔn)喚醒,
    發(fā)表于 09-20 16:38

    Java基礎(chǔ)學(xué)習(xí)多線程使用指南

    黑馬程序員-----Java基礎(chǔ)學(xué)習(xí)多線程
    發(fā)表于 10-08 14:10

    java多線程編程實例 (源程序)

    java多線程編程實例 import java.awt.*;import javax.swing.*; public class CompMover extends Object { 
    發(fā)表于 10-22 11:48 ?0次下載

    java多線程設(shè)計模式_結(jié)城浩

    JAVA多線程設(shè)計模式》通過淺顯易懂的文字與實例來介紹JAVA線程相關(guān)的設(shè)計模式概念,并且通過實際的JAVA程序范例和UML圖示來一一解說
    發(fā)表于 01-05 16:15 ?0次下載
    <b class='flag-5'>java</b><b class='flag-5'>多線程</b>設(shè)計模式_結(jié)城浩

    java多線程同步方法

    二、為什么要線程同步 因為當(dāng)我們有多個線程要同時訪問一個變量或?qū)ο髸r,如果這些線程既有讀又有寫操作時,就會導(dǎo)致變量值或?qū)ο蟮臓顟B(tài)出現(xiàn)混亂,從而導(dǎo)致程序異常。舉個例子,如果一個銀行賬戶
    發(fā)表于 09-27 13:19 ?0次下載

    分析在javasleep和wait的不同

    的監(jiān)控狀態(tài)依然保持者,當(dāng)指定的時間到了又會自動恢復(fù)運行狀態(tài)。 在調(diào)用sleep()方法的過程線程不會釋放對象鎖。 而當(dāng)調(diào)用wait()方法
    發(fā)表于 09-27 14:41 ?0次下載

    Java多線程總結(jié)之Queue

    Java多線程應(yīng)用,隊列的使用率很高,多數(shù)生產(chǎn)消費模型的首選數(shù)據(jù)結(jié)構(gòu)就是隊列。Java提供的線程安全的Queue可以分為 阻塞隊列和非阻
    發(fā)表于 11-28 16:14 ?3270次閱讀
    <b class='flag-5'>Java</b><b class='flag-5'>多線程</b>總結(jié)之Queue

    java學(xué)習(xí)——java面試【事務(wù)、鎖、多線程】資料整理

    本文檔內(nèi)容介紹了基于java學(xué)習(xí)java面試【事務(wù)、鎖、多線程】資料整理,供參考
    發(fā)表于 03-13 13:53 ?0次下載

    Java教程之零點起飛學(xué)Java線程資料說明

    多線程編程是提高應(yīng)用程序性能的重要手段之一。Java平臺從開始就被設(shè)計成為多線程環(huán)境,從語言級上支持多線程。在Java語言中,提供了創(chuàng)建、啟
    發(fā)表于 02-20 10:41 ?3次下載
    <b class='flag-5'>Java</b>教程之零點起飛學(xué)<b class='flag-5'>Java</b>的<b class='flag-5'>線程</b>資料說明

    Java多線程永動任務(wù) 多線程異步任務(wù)項目解讀

    1. 功能說明 2. 多線程任務(wù)示例 2.1 線程池 2.2 單個任務(wù) 2.3 任務(wù)入口 2.4 結(jié)果分析 2.5 源碼地址 3. 寫在最后 大家好,今天教大家擼一個 Java多線程
    的頭像 發(fā)表于 10-19 11:46 ?1057次閱讀

    java實現(xiàn)多線程的幾種方式

    Java實現(xiàn)多線程的幾種方式 多線程是指程序包含了兩個或以上的線程,每個線程都可以并行執(zhí)行不同
    的頭像 發(fā)表于 03-14 16:55 ?512次閱讀