最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java多線程中wait、notify、notifyAll使用詳解

 更新時間:2019年05月08日 14:53:58   作者:君莫笑  
這篇文章主要介紹了Java多線程中wait、notify、notifyAll使用詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

基礎知識

首先我們需要知道,這幾個都是Object對象的方法。換言之,Java中所有的對象都有這些方法。

public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
public final void wait() throws InterruptedException {
  wait(0);
}

其中notify()、notifyAll()、wait(long timeout)是本地方法

其次我們需要知道這幾個方法主要是用來個線程之間通信的。那可能就有人會問,既然是用來線程之間通信的,那為什么這幾個方法不是在線程類Thread上呢?對于這個問題,我們先來看下這幾個方法的具體使用方式再來回答這個問題。

用法

Java中規(guī)定,在調(diào)用者三個方法時,當前線程必須獲得對象鎖。因此就得配合synchronized關鍵字來使用

//使用模式,不代表可運行代碼
synchronized(object) {
  while(contidion) {
    object.wait();
  }
  //object.notify();
  //object.notifyAll();
}

或者

public synchronized void methodName() {
  while(contidion) {
    object.wait();
  }
  //object.notify();
  //object.notifyAll();
}

在synchronized拿到對象鎖之后,synchronized代碼塊或者方法中,必定是會持有對象鎖的,因此就可以使用wait()或者notify()。

通過上述使用方法,我們也能很好理解為什么這幾個方法是在Object上而不是在Thread上。因為每個對象都可以作為synchronized鎖的對象,因此wait、notify等必須和對象關聯(lián)才能配合synchronized使用。

作用

方法 作用
wait 線程自動釋放占有的對象鎖,并等待notify。
notify 隨機喚醒一個正在wait當前對象的線程,并讓被喚醒的線程拿到對象鎖
notifyAll 喚醒所有正在wait當前對象的線程,但是被喚醒的線程會再次去競爭對象鎖。因為一次只有一個線程能拿到鎖,所有其他沒有拿到鎖的線程會被阻塞。推薦使用。

1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。

2、wait()使當前線程阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步代碼塊里使用 wait()、notify/notifyAll() 方法。

3、 由于 wait()、notify/notifyAll() 在synchronized 代碼塊執(zhí)行,說明當前線程一定是獲取了鎖的。

當線程執(zhí)行wait()方法時候,會釋放當前的鎖,然后讓出CPU,進入等待狀態(tài)。

只有當 notify/notifyAll() 被執(zhí)行時候,才會喚醒一個或多個正處于等待狀態(tài)的線程,然后繼續(xù)往下執(zhí)行,直到執(zhí)行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。

也就是說,notify/notifyAll() 的執(zhí)行只是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執(zhí)行情況。所以在編程中,盡量在使用了notify/notifyAll() 后立即退出臨界區(qū),以喚醒其他線程

4、wait() 需要被try catch包圍,中斷也可以使wait等待的線程喚醒。

5、notify 和wait 的順序不能錯,如果A線程先執(zhí)行notify方法,B線程在執(zhí)行wait方法,那么B線程是無法被喚醒的。

6、notify 和 notifyAll的區(qū)別

notify方法只喚醒一個等待(對象的)線程并使該線程開始執(zhí)行。所以如果有多個線程等待一個對象,這個方法只會喚醒其中一個線程,選擇哪個線程取決于操作系統(tǒng)對多線程管理的實現(xiàn)。notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程將會第一個處理取決于操作系統(tǒng)的實現(xiàn)。如果當前情況下有多個線程需要被喚醒,推薦使用notifyAll 方法。比如在生產(chǎn)者-消費者里面的使用,每次都需要喚醒所有的消費者或是生產(chǎn)者,以判斷程序是否可以繼續(xù)往下執(zhí)行。

7、在多線程中要測試某個條件的變化,使用if 還是while?

要注意,notify喚醒沉睡的線程后,線程會接著上次的執(zhí)行繼續(xù)往下執(zhí)行。所以在進行條件判斷時候,可以先把 wait 語句忽略不計來進行考慮,顯然,要確保程序一定要執(zhí)行,并且要保證程序直到滿足一定的條件再執(zhí)行,要使用while來執(zhí)行,以確保條件滿足和一定執(zhí)行。如下代碼:

public class K {
  //狀態(tài)鎖
  private Object lock;
  //條件變量
  private int now,need;
  public void produce(int num){
    //同步
    synchronized (lock){
      //當前有的不滿足需要,進行等待
      while(now < need){
        try {
          //等待阻塞
          wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println("我被喚醒了!");
      }
      // 做其他的事情
    }
  }
}

顯然,只有當前值滿足需要值的時候,線程才可以往下執(zhí)行,所以,必須使用while 循環(huán)阻塞。注意,wait() 當被喚醒時候,只是讓while循環(huán)繼續(xù)往下走.如果此處用if的話,意味著if繼續(xù)往下走,會跳出if語句塊。但是,notifyAll 只是負責喚醒線程,并不保證條件云云,所以需要手動來保證程序的邏輯。

實際案例

接下來我們就使用wait()、notify()來實現(xiàn)一個生產(chǎn)者、消費者模式。這個也是面試過程中可能會被問到的地方。至于什么是生產(chǎn)者消費者模式,不明白的同學請自行百度。

首先是一些基礎的代碼

private static Boolean run = true;//控制是否生產(chǎn)和消費
private static final Integer MAX_CAPACITY = 5;//緩沖區(qū)最大數(shù)量
private static final LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();//緩沖隊列

生產(chǎn)者代碼

/**
 * 生產(chǎn)者
 */
class Producter extends Thread {
  @Override
  public void run() {
    while (run) {
      synchronized (queue) {
        while (queue.size() >= MAX_CAPACITY * 2) {
          try {
            System.out.println("緩沖隊列已滿,等待消費");
            queue.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }

        try {
          String string = UUID.randomUUID().toString();
          queue.put(string);
          System.out.println("生產(chǎn):" + string);
          Thread.sleep(500);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }

        queue.notifyAll();//通知生產(chǎn)者和消費者
      }
    }
  }
}

消費者代碼

/**
 * 消費者
 */
class Consumer extends Thread {
  @Override
  public void run() {
    while (run) {
      synchronized (queue) {
        while (queue.isEmpty()) {
          try {
            System.out.println("隊列為空,等待生產(chǎn)");
            queue.wait();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }

        try {
          System.out.println("消費:" + queue.take());
          Thread.sleep(500);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }

        queue.notifyAll();//通知生產(chǎn)者和消費者
      }
    }
  }
}

代碼說明

1、生產(chǎn)者和消費者都繼承了線程Thread,因為wait、notify本身就是線程間通信使用

2、生產(chǎn)者和消費者都有兩層while,外層的while是用來判斷是否運行生產(chǎn)者和消費者。內(nèi)存的while用來判斷隊列queue是否已滿或者為空,如果滿足條件,則使得當前線程變成等待狀態(tài)(等待notify)。

3、內(nèi)層的條件判斷為什么用while不用if,原因是當線程被wait之后,會釋放對象鎖。當?shù)却木€程被notify之后,必須再次嘗試去獲取對象鎖,如果沒有獲取到對象鎖,那還必須等待,直到拿到對象鎖之后才能向后執(zhí)行。

4、當生產(chǎn)者生產(chǎn)了一個數(shù)據(jù)或者消費者消費了一個數(shù)據(jù)之后,使用notifyAll()方法來通知所有等待當前對象鎖的線程,但是一次只會有一個等待的線程能拿到鎖。

5、我們使用queue作為鎖的對象在不同線程之間進行通信

代碼運行結果

隊列為空,等待生產(chǎn)
生產(chǎn):e422484e-8eb3-4a91-8a7c-97e21d7ef498
生產(chǎn):7894b802-2529-4798-ba98-9f0657f39240
生產(chǎn):848f6759-a427-4a94-89dc-3f484daaa467
生產(chǎn):f711d3dc-972c-4c44-8640-faffe376d354
生產(chǎn):38a08e62-d774-4ed5-8b51-f1ad534c246f
消費:e422484e-8eb3-4a91-8a7c-97e21d7ef498
消費:7894b802-2529-4798-ba98-9f0657f39240
消費:848f6759-a427-4a94-89dc-3f484daaa467
消費:f711d3dc-972c-4c44-8640-faffe376d354
消費:38a08e62-d774-4ed5-8b51-f1ad534c246f
隊列為空,等待生產(chǎn)
生產(chǎn):9fae26f3-0b6e-4fbd-9620-040667efe0af
生產(chǎn):95bb1d88-e08a-4f70-a270-f75760994184
消費:9fae26f3-0b6e-4fbd-9620-040667efe0af
消費:95bb1d88-e08a-4f70-a270-f75760994184
隊列為空,等待生產(chǎn)
生產(chǎn):13d304bc-dff3-44a4-9527-2e0facd884e7
生產(chǎn):2693e069-bae1-4beb-adcd-a3c3bf5d232b
消費:13d304bc-dff3-44a4-9527-2e0facd884e7
消費:2693e069-bae1-4beb-adcd-a3c3bf5d232b

由結果也可以看出來,生產(chǎn)和消費是無規(guī)則的,因為雖然notifyAll()通知了所有的等待線程,但是不確定那個線程中能拿到對象鎖。但是也有一個很明顯的問題,因為同時只有一個線程能拿到對象鎖,生產(chǎn)者和消費者不可能同時運行。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • java構造方法的作用總結

    java構造方法的作用總結

    在本篇文章里小編給大家整理了關于java構造方法的相關知識點以及實例代碼,有需要的朋友們可以學習下。
    2019-07-07
  • Spring計時器StopWatch使用示例

    Spring計時器StopWatch使用示例

    這篇文章主要介紹了Spring計時器StopWatch使用示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • Java反轉字符串的10種方法

    Java反轉字符串的10種方法

    這篇文章主要介紹了Java反轉字符串的10種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,下面我們來一起學習一下吧
    2019-06-06
  • java中Class.forName的作用淺談

    java中Class.forName的作用淺談

    這篇文章介紹了java中Class.forName的作用,有需要的朋友可以參考一下
    2013-11-11
  • 分析HashMap 的 JDK 源碼

    分析HashMap 的 JDK 源碼

    這篇文章主要分析了HashMap 的 JDK 源碼,幫助大家更好的理解和學習Java,感興趣的朋友可以了解下
    2020-10-10
  • springboot啟動報錯:application?startup?failed問題

    springboot啟動報錯:application?startup?failed問題

    這篇文章主要介紹了springboot啟動報錯:application?startup?failed問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Springboot基礎之RedisUtils工具類

    Springboot基礎之RedisUtils工具類

    本文來說下RedisUtils工具類,主要介紹了整合Redis、MyBatis,封裝RedisUtils工具類等知識,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-05-05
  • java中abstract修改類的實例方法

    java中abstract修改類的實例方法

    在本篇文章里小編給各位分享了一篇關于java中abstract修改類的實例方法,有需要的朋友們可以學習下。
    2020-12-12
  • 通過代碼實例解析JAVA類生命周期

    通過代碼實例解析JAVA類生命周期

    這篇文章主要介紹了通過代碼實例解析JAVA類生命周期,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • Java中的getClass()以及getName()方法使用

    Java中的getClass()以及getName()方法使用

    這篇文章主要介紹了Java中的getClass()以及getName()方法使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評論

信宜市| 南充市| 沾益县| 祁东县| 奉节县| 仪陇县| 内江市| 大同县| 高雄县| 清苑县| 镇原县| 运城市| 康马县| 永川市| 盐亭县| 峡江县| 平南县| 四会市| 淮安市| 称多县| 曲水县| 桐城市| 黄龙县| 雷山县| 庐江县| 绿春县| 来安县| 府谷县| 苏尼特右旗| 大新县| 横峰县| 修文县| 营口市| 吉隆县| 利辛县| 孝感市| 巩留县| 龙山县| 金昌市| 神池县| 静安区|