溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

wait和notify和消費(fèi)者生產(chǎn)者的示例分析

發(fā)布時(shí)間:2021-08-03 14:06:19 來(lái)源:億速云 閱讀:101 作者:Leah 欄目:大數(shù)據(jù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)wait和notify和消費(fèi)者生產(chǎn)者的示例分析,文章內(nèi)容豐富且以專(zhuān)業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。


   首先,調(diào)用一個(gè)Object的wait與notify/notifyAll的時(shí)候,必須保證調(diào)用代碼對(duì)該Object是同步的,也就是說(shuō)必須在作用等同于synchronized(obj){......}的內(nèi)部才能夠去調(diào)用obj的wait與notify/notifyAll三個(gè)方法,否則就會(huì)報(bào)錯(cuò):
  java.lang.IllegalMonitorStateException:current thread not owner
  在調(diào)用wait的時(shí)候,線程自動(dòng)釋放其占有的對(duì)象鎖,同時(shí)不會(huì)去申請(qǐng)對(duì)象鎖。當(dāng)線程被喚醒的時(shí)候,它才再次獲得了去獲得對(duì)象鎖的權(quán)利。
  所以,notify與notifyAll沒(méi)有太多的區(qū)別,只是notify僅喚醒一個(gè)線程并允許它去獲得鎖,notifyAll是喚醒所有等待這個(gè)對(duì)象的線程并允許它們?nèi)カ@得對(duì)象鎖,只要是在synchronied塊中的代碼,沒(méi)有對(duì)象鎖是寸步難行的。其實(shí)喚醒一個(gè)線程就是重新允許這個(gè)線程去獲得對(duì)象鎖并向下運(yùn)行。

   notifyAll,雖然是對(duì)每個(gè)wait的對(duì)象都調(diào)用一次notify,但是這個(gè)還是有順序的,每個(gè)對(duì)象都保存這一個(gè)等待對(duì)象鏈,調(diào)用的順序就是這個(gè)鏈的順序。其實(shí)啟動(dòng)等待對(duì)象鏈中各個(gè)線程的也是一個(gè)線程,在具體應(yīng)用的時(shí)候,需要注意一下。

  wait(),notify(),notifyAll()不屬于Thread類(lèi),而是屬于Object基礎(chǔ)類(lèi),也就是說(shuō)每個(gè)對(duì)像都有wait(),notify(),notifyAll()的功能。因?yàn)槎紓€(gè)對(duì)像都有鎖,鎖是每個(gè)對(duì)像的基礎(chǔ),當(dāng)然操作鎖的方法也是最基礎(chǔ)了。

wait():

等待對(duì)象的同步鎖,需要獲得該對(duì)象的同步鎖才可以調(diào)用這個(gè)方法,否則編譯可以通過(guò),但運(yùn)行時(shí)會(huì)收到一個(gè)異常:IllegalMonitorStateException。

調(diào)用任意對(duì)象的 wait() 方法導(dǎo)致該線程阻塞,該線程不可繼續(xù)執(zhí)行,并且該對(duì)象上的鎖被釋放。

notify():

喚醒在等待該對(duì)象同步鎖的線程(只喚醒一個(gè),如果有多個(gè)在等待),注意的是在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且不是按優(yōu)先級(jí)。

調(diào)用任意對(duì)象的notify()方法則導(dǎo)致因調(diào)用該對(duì)象的 wait()方法而阻塞的線程中隨機(jī)選擇的一個(gè)解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)。

notifyAll():

喚醒所有等待的線程,注意喚醒的是notify之前wait的線程,對(duì)于notify之后的wait線程是沒(méi)有效果的。

通常,多線程之間需要協(xié)調(diào)工作:如果條件不滿(mǎn)足,則等待;當(dāng)條件滿(mǎn)足時(shí),等待該條件的線程將被喚醒。在Java中,這個(gè)機(jī)制的實(shí)現(xiàn)依賴(lài)于wait/notify。等待機(jī)制與鎖機(jī)制是密切關(guān)聯(lián)的。

例如:
  synchronized(obj) {
  while(!condition) {
  obj.wait();
  }
  obj.doSomething();
  }
  
  當(dāng)線程A獲得了obj鎖后,發(fā)現(xiàn)條件condition不滿(mǎn)足,無(wú)法繼續(xù)下一處理,于是線程A就wait()。
  在另一線程B中,如果B更改了某些條件,使得線程A的condition條件滿(mǎn)足了,就可以喚醒線程A :
  
  synchronized(obj) {
  condition = true;
  obj.notify();
  }
  
  需要注意的概念是:
  # 調(diào)用obj的wait(), notify()方法前,必須獲得obj鎖,也就是必須寫(xiě)在synchronized(obj){...} 代碼段內(nèi)。
  # 調(diào)用obj.wait()后,線程A就釋放了obj的鎖,否則線程B無(wú)法獲得obj鎖,也就無(wú)法在synchronized(obj){...} 代碼段內(nèi)喚醒A。
  # 當(dāng)obj.wait()方法返回后,線程A需要再次獲得obj鎖,才能繼續(xù)執(zhí)行。
  #如果A1,A2,A3都在obj.wait(),則B調(diào)用obj.notify()只能喚醒A1,A2,A3中的一個(gè)(具體哪一個(gè)由JVM決定)?!?br/>   #obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續(xù)執(zhí)行obj.wait()的下一條語(yǔ)句,必須獲得obj鎖,因此,A1,A2,A3只有一個(gè)有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行,例如A1,其余的需要等待A1釋放obj鎖之后才能繼續(xù)執(zhí)行。
  # 當(dāng)B調(diào)用obj.notify/notifyAll的時(shí)候,B正持有obj鎖,因此,A1,A2,A3雖被喚醒,但是仍無(wú)法獲得obj鎖。直到B退出synchronized塊,釋放obj鎖后,A1,A2,A3中的一個(gè)才有機(jī)會(huì)獲得鎖繼續(xù)執(zhí)行。

談一下synchronized和wait()、notify()等的關(guān)系:

1.有synchronized的地方不一定有wait,notify

2.有wait,notify的地方必有synchronized.這是因?yàn)閣ait和notify不是屬于線程類(lèi),而是每一個(gè)對(duì)象都具有的方法,而且,這兩個(gè)方法都和對(duì)象鎖有關(guān),有鎖的地方,必有synchronized。

另外,注意一點(diǎn):如果要把notify和wait方法放在一起用的話,必須先調(diào)用notify后調(diào)用wait,因?yàn)槿绻{(diào)用完wait,該線程就已經(jīng)不是currentthread了。

使用wait和notify實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者

1. 使用注意事項(xiàng)
永遠(yuǎn)在synchronized的函數(shù)或?qū)ο罄锸褂脀ait、notify和notifyAll,不然Java虛擬機(jī)會(huì)生成IllegalMonitorStateException。
永遠(yuǎn)在while循環(huán)里而不是if語(yǔ)句下使用wait。這樣,循環(huán)會(huì)在線程睡眠前后都檢查wait的條件,并在條件實(shí)際上并未改變的情況下處理喚醒通知。
永遠(yuǎn)在多線程間共享的對(duì)象上使用wait。
notify隨機(jī)通知一個(gè)阻塞在對(duì)象上的線程;notifyAll通知阻塞在對(duì)象上所有的線程。
2. 代碼示例
2.1 生產(chǎn)者

public class Producer implements Runnable{

    private Queue<Integer> queue;
    private int maxSize;

    public Producer(Queue<Integer> queue, int maxSize){
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        while (true){
            synchronized (queue){
                while (queue.size() == maxSize){
                    try{
                        System.out.println("Queue is Full");
                        queue.wait();
                    }catch (InterruptedException ie){
                        ie.printStackTrace();
                    }
                }
                Random random = new Random();
                int i = random.nextInt();
                System.out.println("Produce " + i);
                queue.add(i);
                queue.notifyAll();
            }
        }
    }
}

2.2 消費(fèi)者
public class Consumer implements Runnable{

    private Queue<Integer> queue;
    private int maxSize;

    public Consumer(Queue<Integer> queue, int maxSize){
        this.queue = queue;
        this.maxSize = maxSize;
    }

    @Override
    public void run() {
        while (true){
            synchronized (queue){
                while (queue.isEmpty()){
                    System.out.println("Queue is Empty");
                    try{
                        queue.wait();
                    }catch (InterruptedException ie){
                        ie.printStackTrace();
                    }
                }
                int v = queue.remove();
                System.out.println("Consume " + v);
                queue.notifyAll();
            }
        }
    }
}
2.3 Main
public class Main {
    public static void main(String[] args){
        Queue<Integer> queue = new LinkedList<>();
        int maxSize = 10;
        Producer p = new Producer(queue, maxSize);
        Consumer c = new Consumer(queue, maxSize);
        Thread pT = new Thread(p);
        Thread pC = new Thread(c);
        pT.start();
        pC.start();
    }
}
 

上述就是小編為大家分享的wait和notify和消費(fèi)者生產(chǎn)者的示例分析了,如果剛好有類(lèi)似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI