<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 9293字,需瀏覽 19分鐘

           ·

          2023-06-20 14:32

          胖虎和朋友原創(chuàng)的視頻教程有興趣的可以看看


          (文末附課程大綱)


          ??2023 最新,Java成神之路,架構(gòu)視頻(點(diǎn)擊查看)


          ??超全技術(shù)棧的Java入門+進(jìn)階+實(shí)戰(zhàn)!(點(diǎn)擊查看)

          一、線程間等待與喚醒機(jī)制

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

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

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

          二、等待方法wait()

          wait 做的事情:

          • 使當(dāng)前執(zhí)行代碼的線程進(jìn)行等待. (把線程放到等待隊(duì)列中)
          • 釋放當(dāng)前的鎖
          • 滿足一定條件時(shí)被喚醒, 重新嘗試獲取這個(gè)鎖.

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

          圖片

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

          • 其他線程調(diào)用該對(duì)象的 notify 方法.
          • wait 等待時(shí)間超時(shí) (wait 方法提供一個(gè)帶有 timeout 參數(shù)的版本, 來指定等待時(shí)間).
          • 其他線程調(diào)用該等待線程的 interrupted 方法, 導(dǎo)致 wait 拋出 InterruptedException 異常.

          注意事項(xiàng):

          • 調(diào)用wait()方法的前提是首先要獲取該對(duì)象的鎖(synchronize對(duì)象鎖)
          • 調(diào)用wait()方法會(huì)釋放鎖,本線程進(jìn)入等待隊(duì)列等待被喚醒,被喚醒后不是立即恢復(fù)執(zhí)行,而是進(jìn)入阻塞隊(duì)列,競(jìng)爭(zhēng)鎖

          等待方法:

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

          圖片

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

          圖片

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

          圖片

          三、喚醒方法notify()

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

          • 方法notify()也要在同步方法或同步塊中調(diào)用,該方法是用來通知那些可能等待該對(duì)象的對(duì)象鎖的其它線程,對(duì)其發(fā)出通知notify,并使它們重新獲取該對(duì)象的對(duì)象鎖。
          • 如果有多個(gè)線程等待,則有線程調(diào)度器隨機(jī)挑選出一個(gè)呈 wait 狀態(tài)的線程。(并沒有 “先來后到”)
          • notify()方法后,當(dāng)前線程不會(huì)馬上釋放該對(duì)象鎖,要等到執(zhí)行notify()方法的線程將程序執(zhí)行完,也就是退出同步代碼塊之后才會(huì)釋放對(duì)象鎖。

          注意事項(xiàng):

          • notify():隨機(jī)喚醒一個(gè)處在等待狀態(tài)的線程。
          • notifyAll():喚醒所有處在等待狀態(tài)的線程。

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

          圖片

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

          對(duì)于wait和notify方法,其實(shí)有一個(gè)阻塞隊(duì)列也有一個(gè)等待隊(duì)列。

          • 阻塞隊(duì)列表示同一時(shí)間只有一個(gè)線程能獲取到鎖,其他線程進(jìn)入阻塞隊(duì)列
          • 等待隊(duì)列表示調(diào)用wait (首先此線程要獲取到鎖,進(jìn)入等待隊(duì)列,釋放鎖)

          舉個(gè)栗子:

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

          private static class WaitTask implements Runnable {
                  private Object lock;
                  public WaitTask(Object lock) {
                      this.lock = lock;
                  }
                  @Override
                  public void run() {
                      synchronized (lock) {
                          System.out.println(Thread.currentThread().getName() + "準(zhǔn)備進(jìn)入等待狀態(tài)");
                          // 此線程在等待lock對(duì)象的notify方法喚醒
                          try {
                              lock.wait();
                              Thread.sleep(1000);
                          } catch (InterruptedException e) {
                              throw new RuntimeException(e);
                          }
                          System.out.println(Thread.currentThread().getName() + "等待結(jié)束,本線程繼續(xù)執(zhí)行");
                      }
                  }
              }

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

          圖片

          由于同一時(shí)間只有一個(gè)線程(隨機(jī)調(diào)度)能獲取到synchronized鎖,所以會(huì)有兩個(gè)線程沒競(jìng)爭(zhēng)到鎖,從而進(jìn)入了阻塞隊(duì)列。

          這里假如t2先競(jìng)爭(zhēng)到了鎖,所以先會(huì)阻塞t1和t3:

          圖片

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

          圖片

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

          這里假如t3 競(jìng)爭(zhēng)到了鎖,阻塞隊(duì)列只剩下t1:

          圖片

          然后t3運(yùn)行到了wait方法,釋放鎖,然后進(jìn)入等待隊(duì)列:

          圖片

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

          圖片

          重點(diǎn)來了:

          在等待隊(duì)列中的線程,被notify喚醒之后,會(huì)直接回到阻塞隊(duì)列去競(jìng)爭(zhēng)鎖!??!而不是直接喚醒~

          舉個(gè)栗子:

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

          圖片

          然后再去競(jìng)爭(zhēng)這個(gè)鎖,去執(zhí)行wait之后的代碼~~

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

          private static class WaitTask implements Runnable {
                  private Object lock;
                  public WaitTask(Object lock) {
                      this.lock = lock;
                  }
                  @Override
                  public void run() {
                      synchronized (lock) {
                          System.out.println(Thread.currentThread().getName() + "準(zhǔn)備進(jìn)入等待狀態(tài)");
                          // 此線程在等待lock對(duì)象的notify方法喚醒
                          try {
                              lock.wait();
                              Thread.sleep(1000);
                          } catch (InterruptedException e) {
                              throw new RuntimeException(e);
                          }
                          System.out.println(Thread.currentThread().getName() + "等待結(jié)束,本線程繼續(xù)執(zhí)行");
                      }
                  }
              }
              private static class NotifyTask implements Runnable {
                  private Object lock;
                  public NotifyTask(Object lock) {
                      this.lock = lock;
                  }
                  @Override
                  public void run() {
                      synchronized (lock) {
                          System.out.println("準(zhǔn)備喚醒");
                          // 喚醒所有線程(隨機(jī))
                          lock.notifyAll();
                          System.out.println("喚醒結(jié)束");
                      }
                  }
              }

              public static void main(String[] args) throws InterruptedException {
                  Object lock = new Object();
                  Object lock2 = new Object();
                  // 創(chuàng)建三個(gè)等待線程
                  Thread t1 = new Thread(new WaitTask(lock),"t1");
                  Thread t2 = new Thread(new WaitTask(lock),"t2");
                  Thread t3 = new Thread(new WaitTask(lock),"t3");
                 // 創(chuàng)建一個(gè)喚醒線程
                  Thread notify = new Thread(new NotifyTask(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方法會(huì)釋放鎖,線程進(jìn)入WAITING狀態(tài),等待被其他線程喚醒或者超時(shí)自動(dòng)喚醒,喚醒之后的線程需要再次競(jìng)爭(zhēng)synchronized鎖才能繼續(xù)執(zhí)行。

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

          總結(jié)

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

          來源:blog.csdn.net/qq_43575801/article/details/127601039


          胖虎聯(lián)合兩位大佬朋友,知名培訓(xùn)機(jī)構(gòu)講師和科大訊飛架構(gòu),聯(lián)合打造了《Java架構(gòu)師成長(zhǎng)之路》的視頻教程。完全對(duì)標(biāo)外面2萬左右的培訓(xùn)課程。

          除了基本的視頻教程之外,還提供了超詳細(xì)的課堂筆記,以及源碼等資料包..


          目前課程內(nèi)測(cè)活動(dòng)價(jià):999元,后續(xù)可能會(huì)上調(diào)至 2999元


          點(diǎn)擊下方超鏈接查看詳情(或者點(diǎn)擊文末閱讀原文):

          (點(diǎn)擊查看)  2023年,最新Java架構(gòu)師成長(zhǎng)之路 視頻教程!

          以下是課程大綱,大家可以長(zhǎng)按識(shí)別查看!


          瀏覽 31
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  玖玖在线免费观看视频 | 99成人| 久久免费视频99 | 婷婷五月天大香蕉 | 亚洲欧洲色|