衡阳派盒市场营销有限公司

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

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

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

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

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

多線程并發的場景下,有時需要某些線程先執行,這些線程執行結束后其他線程再繼續執行。

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

二、等待方法wait()

wait 做的事情:

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

釋放當前的鎖

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

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

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

wait 結束等待的條件:

其他線程調用該對象的 notify 方法.

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

其他線程調用該等待線程的 interrupted 方法, 導致 wait 拋出 InterruptedException 異常.

注意事項:

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

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

等待方法:

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

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

2.等待一段時間,若在該時間內線程被喚醒,則繼續執行,若超過相應時間還沒有其他線程喚醒此線程,此線程不再等待,恢復執行。

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

調用wait方法之后:

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

三、喚醒方法notify()

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

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

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

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

注意事項:

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

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

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

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

四、關于wait和notify內部等待問題(重要)

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

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

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

舉個栗子:

現有如下定義的等待線程任務

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

然后創建三個等待線程:

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

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

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

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

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

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

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

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

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

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

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

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

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

重點來了:

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

舉個栗子:

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

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

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

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

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

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

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

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

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

總結

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

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    19

    文章

    2975

    瀏覽量

    105150
  • 多線程
    +關注

    關注

    0

    文章

    278

    瀏覽量

    20075
  • 代碼
    +關注

    關注

    30

    文章

    4828

    瀏覽量

    69055
  • 線程
    +關注

    關注

    0

    文章

    505

    瀏覽量

    19758
  • WAIT
    +關注

    關注

    0

    文章

    4

    瀏覽量

    2528

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

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

收藏 人收藏

    評論

    相關推薦

    Java多線程的用法

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

    Javasleep和wait的區別

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

    Java線程阻塞方法大全

    ()方法,當前線程轉A入阻塞狀態,直到線程B運行結束,線程A才由阻塞狀態轉為可執行狀態。以上是Java
    發表于 04-02 15:42

    Java線程喚醒與阻塞規則

    的join()方法,當前線程轉A入阻塞狀態,直到線程B運行結束,線程A才由阻塞狀態轉為可執行狀態。以上是Java
    發表于 07-06 15:11

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

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

    Java基礎學習多線程使用指南

    黑馬程序員-----Java基礎學習多線程
    發表于 10-08 14:10

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

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

    java多線程設計模式_結城浩

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

    java多線程同步方法

    二、為什么要線程同步 因為當我們有多個線程要同時訪問一個變量或對象時,如果這些線程既有讀又有寫操作時,就會導致變量值或對象的狀態出現混亂,從而導致程序異常。舉個例子,如果一個銀行賬戶
    發表于 09-27 13:19 ?0次下載

    分析在javasleep和wait的不同

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

    Java多線程總結之Queue

    Java多線程應用,隊列的使用率很高,多數生產消費模型的首選數據結構就是隊列。Java提供的線程安全的Queue可以分為 阻塞隊列和非阻
    發表于 11-28 16:14 ?3371次閱讀
    <b class='flag-5'>Java</b><b class='flag-5'>多線程</b>總結之Queue

    java學習——java面試【事務、鎖、多線程】資料整理

    本文檔內容介紹了基于java學習java面試【事務、鎖、多線程】資料整理,供參考
    發表于 03-13 13:53 ?0次下載

    Java教程之零點起飛學Java線程資料說明

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

    Java多線程永動任務 多線程異步任務項目解讀

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

    java實現多線程的幾種方式

    Java實現多線程的幾種方式 多線程是指程序包含了兩個或以上的線程,每個線程都可以并行執行不同
    的頭像 發表于 03-14 16:55 ?782次閱讀
    大发888娱乐场下载iypuqrd| 大赢家百家乐官网66| 电子百家乐官网假在线哪| 百家乐旺门打法| 在线真人娱乐| 做生意店子内风水布置| 大发888 网站被攻击了| qq百家乐官网网络平台| 至尊百家乐赌场娱乐网规则| 海立方百家乐官网赢钱| 缅甸百家乐赌城| 利赢百家乐官网现金网| 百家乐双层筹码盘| 优博国际| 百家乐足球投注网哪个平台网址测速最好| 佳豪娱乐| 百家乐系统足球博彩通| 永利高百家乐官网进不去| 玩百家乐有几种公式| 优博家百家乐官网娱乐城| 太阳城王子酒店| 百家乐官网策略网络游戏信誉怎么样| 大发888开户博盈国际| 百家乐官网怎么稳赚| 百家乐筹码皇冠| 微信百家乐官网群二维码| 保康县| 百家乐免费体验金| 博彩旅游业| 阳宅24方位判断方法| 皇冠百家乐官网代理网| 百家乐和抽水官网| 威尼斯人娱乐城官网地址| 五星百家乐官网的玩法技巧和规则 | 百家乐任你博娱乐场开户注册| 百家乐官网扎金花斗地主| 蓝田县| 棋牌游戏中心| 红桃K百家乐的玩法技巧和规则| 风水24山| 百家乐官网发脾机|