1. java中的notify和notifyAll有什麼區別
首先從名字可以了解,notify是通知一個線程獲取鎖,notifyAll是通知所有相關的線程去競爭鎖。
notify不能保證獲得鎖的線程,真正需要鎖,並且可能產生死鎖。
舉例1:
所有人(消費者線程)准備吃飯,食堂沒有開放(沒有釋放鎖)打飯窗口(鎖),所有人等待(WAITING)。
食堂開飯打飯窗口(釋放鎖),並廣播消息「開飯了」(notifyAll),所有人競爭排隊,並等待吃飯(BLOCKED)。每一個人依次在打飯窗口(獲得鎖)打飯(RUNNABLE)。如果想吃飯就打完飯後離開(釋放鎖),不想吃飯就直接離開(釋放鎖)。如果吃完了還想吃,就主動等待下一次「開飯了」的消息(wait)。
食堂通知一個人來吃飯(notify),此人來到打飯窗口(獲得鎖)打飯(RUNNABLE),其他人都在等待開飯的消息(WAITING)。如果想吃飯就打完飯後離開(釋放鎖),不想吃飯就直接離開(釋放鎖)。如果吃完了還想吃,就主動等待下一次「開飯」的消息(WAITING)。
notify不能保證通知到真正想吃飯的人。
舉例2:
兩個生產者P1、P2,兩個消費者C1、C2,共同操作一個隊列,隊列最大長度為1。
開始P1、P2、C1、C2都處於運行狀態(RUNNABLE)。
C1先獲得鎖,P1、P2、C2為BLOCKED狀態。C1發現隊列為空,主動進入WAITING。C2接著獲得鎖,成為RUNNABLE狀態,發現隊列為空,主動進入WAITING。
P1接著獲得鎖,成為RUNNABLE狀態,在隊列中插入一個元素,notify到了另一個生產者P2。P1循環生產,發現隊列不為空,成為WAITING。
P2成為RUNNABLE狀態,發現隊列有值,主動進入WAITING。
此時鎖已被釋放,但P1、P2、C1、C2都處於WAITING狀態,沒有線程去獲取鎖,死了。
notify()和notifyAll()都是Object對象用於通知處在等待該對象的線程的方法。兩者的最大區別在於:
notifyAll使所有原來在該對象上等待被notify的線程統統退出wait的狀態,變成等待該對象上的鎖,一旦該對象被解鎖,他們就會去競爭。
notify則文明得多他只是選擇一個wait狀態線程進行通知,並使它獲得該對象上的鎖,但不驚動其他同樣在等待被該對象notify的線程們,當第一個線程運行完畢以後釋放對象上的鎖此時如果該對象沒有再次使用notify語句,則即便該對象已經空閑,其他wait狀態等待的線程由於沒有得到該對象的通知,繼續處在wait狀態,直到這個對象發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。
下面是一個很好的例子:
importjava.util.*;
classWidget...{}
classWidgetMakerextendsThread...{
List<Widget>finishedWidgets=newArrayList<Widget>();
publicvoidrun()...{
try...{
while(true)...{
Thread.sleep(5000);//actbusy
Widgetw=newWidget();
//也就是說需要5秒鍾才能新產生一個Widget,這決定了一定要用notify而不是notifyAll
//因為上面兩行代碼不是同步的,如果用notifyAll則所有線程都企圖沖出wait狀態
//第一個線程得到了鎖,並取走了Widget(這個過程的時間小於5秒,新的Widget還沒有生成)
//並且解開了鎖,然後第二個線程獲得鎖(因為用了notifyAll其他線程不再等待notify語句
//,而是等待finishedWidgets上的鎖,一旦鎖放開了,他們就會競爭運行),運行
//finishedWidgets.remove(0),但是由於finishedWidgets現在還是空的,
//於是產生異常
//***********這就是為什麼下面的那一句不能用notifyAll而是要用notify
synchronized(finishedWidgets)...{
finishedWidgets.add(w);
finishedWidgets.notify();//這里只能是notify而不能是notifyAll
}
}
}
catch(InterruptedExceptione)...{}
}
publicWidgetwaitForWidget()...{
synchronized(finishedWidgets)...{
if(finishedWidgets.size()==0)...{
try...{
finishedWidgets.wait();
}
catch(InterruptedExceptione)
...{}
}
returnfinishedWidgets.remove(0);
}
}
}
...{
privateWidgetMakermaker;
publicWidgetUser(Stringname,WidgetMakermaker)...{
super(name);
this.maker=maker;
}
publicvoidrun()...{
Widgetw=maker.waitForWidget();
System.out.println(getName()+"gotawidget");
}
publicstaticvoidmain(String[]args)...{
WidgetMakermaker=newWidgetMaker();
maker.start();
newWidgetUser("Lenny",maker).start();
newWidgetUser("Moe",maker).start();
newWidgetUser("Curly",maker).start();
}
}
2. java notify() 與 notifyAll() 區別 以及前者的用法 舉例
首先:使用wait方法和使用synchornized來分配cpu時間是有本質區別的。wait會釋放鎖,synchornized不釋放鎖。
還有:(wait/notify/notifyAll)只能在取得對象鎖的時候才能調用。
調用notifyAll通知所有線程繼續執行,只能有一個線程在執行其餘的線程在等待(因頃帶為在所有線程被喚醒的時候在synchornized塊中)。這時的等待和調用雀行蘆notifyAll前的等待是不一樣的。
notifyAll前:在對象上休息區內休息
notifyAll後:帶埋在排隊等待獲得對象鎖。
notify和notifyAll都是把某個對象上休息區內的線程喚醒,notify只能喚醒一個,但究竟是哪一個不能確定,而notifyAll則喚醒這個對象上的休息室中所有的線程.
一般有為了安全性,我們在絕對多數時候應該使用notifiAll(),除非你明確知道只喚醒其中的一個線程.
至於有些書上說「notify:喚醒同一對象監視器中調用wait的第一個線程」我認為是沒有根據的因為sun公司是這樣說的「The choice is arbitrary and occurs at the discretion of the implementation.」
3. Java中notify和notifyAll的區別
notify方法只喚醒一個等待(對象的)線程並使該線程開始執行。所以如果有多個線程等待一個對象李逗,這個方法只會喚醒其中一個線程,選擇哪個線程取決於操作系統對哪肢賣多線程管理的實現。notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程將會第一個處理取決於操作系統的實現。這些方法可以使用於「生產者-消費者」問題,消費者是在隊列中等待對象的線程,生產者是在隊列中釋飢沖放對象並通知其他線程的線程。
4. Java中notifyAll()方法的實際作用
在Java語言中notifyAll()方法的實際作用如下:
1.notifyAll():Wakes up all threads that are waiting on this object's monitor;
2.當一個線程使用的同步方法中用到某個變數,而此變數又需要其它線程修改後才能符合本線程的需要,則可以在同步方法中調用wait()方法,使本線程等待,並允許其它線程調用這個同步方或譽寬法
3.其它線程在使用這個同步方法不需要等待,當它使用完這個同步方法時,用衫亮notifyAll()通知所有由於使用這個同步方法而處於等待的虛滾線程結束,再次使用這個同步方法
4.如果使第一個處於等待的線程結束等待,則調用方法notify()
5. java中notify怎麼使用
notify(),notifyAll()都是要喚醒正在等待的線程,前者明確喚醒一個,後者喚醒全部。
當程序不明確知道下一個要喚醒的線程時,需要採用notifyAll()喚醒所有在wait池中的線程,讓它們競爭而獲取資源的執行權,但使用notifyAll()時,會出現死鎖的風險,因此,如果程序中明確知道下一個要喚醒的線程時,盡可能使用notify()而非notifyAll()。
6. 幫忙解釋一下java中類的notify的含義,多謝了
1、notify()方法的含義:
(1)notify()方法是隨機喚醒一個線程,將等待隊列中的一個等待線程從等待隊列中移到同步隊列中。
(2)在執行完notify()方法後,當前線程不會馬上釋放該對象鎖,呈wait狀態的線程也不能馬上獲得該對象鎖。
要等到執行notify方法的線程將程序執行完 ,也就是退出sychronized代碼塊後,當前線程才會釋放鎖。而在同步隊列中的該線程才可以獲取該對象鎖。
2、對象所釋放的三個場景:
(1)執行完同步代碼塊就會釋放對象鎖;
(2)在執行代碼塊的過程中,遇到異常而導致線程終止,也會釋放對象鎖;
(3)在執行同步代碼塊的過程中,執行了鎖所屬對象的wait()方法,這個線程會釋放對象鎖,而此線程對象會進入線程等待池中,等待被喚醒。
(6)javanotifyall擴展閱讀
Java中notify和notifyAll的區別:
Java提供了兩個方法notify和notifyAll來喚醒在某些條件下等待的線程,你可以使用它們中的任何一個,但是Java中的notify和notifyAll之間存在細微差別,這使得它成為Java中流行的多線程面試問題之一。
當你調用notify時,只有一個等待線程會被喚醒而且它不能保證哪個線程會被喚醒,這取決於線程調度器。
雖然如果你調用notifyAll方法,那麼等待該鎖的所有線程都會被喚醒,但是在執行剩餘的代碼之前,所有被喚醒的線程都將爭奪鎖定,這就是為什麼在循環上調用wait。
因為如果多個線程被喚醒,那麼線程是將獲得鎖定將首先執行,它可能會重置等待條件,這將迫使後續線程等待。
因此,notify和notifyAll之間的關鍵區別在於notify()只會喚醒一個線程,而notifyAll方法將喚醒所有線程。
7. 如何在 Java 中正確使用 wait,notify 和 notifyAll
wait, notify 和 notifyAll,這些在多線程中被經常用到的保留關鍵字,在實際開發的時候很多時候卻並沒有被大家重視。本文對這些關鍵字的使用進行了描述。
在 Java 中可以用 wait、notify 和 notifyAll
來實現線程間的通信。。舉個例子,如果你的Java程序中有兩個線程——即生產者和消費者,那麼生產者可以通知消費者,讓消費者開始消耗數據,因為隊列緩
沖區中有內容待消費(不為空)。相應的,消費者可以通知生產者可以開始生成更多的數據,因為當它消耗掉某些數據後緩沖區不再為滿。
我們可以利用wait()來讓一個線程在某些條件下暫停運行。例如,在生產者消費者模型中,生產者線程在緩沖區為滿的時候,消費者在緩沖區為空的時
候,都應該暫停運行。如果某些線程在等待某些條件觸發,那當那些條件為真時,你可以用 notify 和 notifyAll
來通知那些等待中的線程重新開始運行。不同之處在於,notify 僅僅通知一個線程,並且我們不知道哪個線程會收到通知,然而 notifyAll
會通知所有等待中的線程。換言之,如果只有一個線程在等待一個信號燈,notify和notifyAll都會通知到這個線程。但如果多個線程在等待這個信
號燈,那麼notify只會通知到其中一個,而其它線程並不會收到任何通知,而notifyAll會喚醒所有等待中的線程。
在這篇文章中你將會學到如何使用 wait、notify 和 notifyAll 來實現線程間的通信,從而解決生產者消費者問題。如果你想要更深入地學習Java中的多線程同步問題,我強烈推薦閱讀Brian Goetz所著的《Java Concurrency in Practice | Java 並發實踐》,不讀這本書你的 Java 多線程征程就不完整哦!這是我最向Java開發者推薦的書之一。
如何使用Wait
盡管關於wait和notify的概念很基礎,它們也都是Object類的函數,但用它們來寫代碼卻並不簡單。如果你在面試中讓應聘者來手寫代碼,
用wait和notify解決生產者消費者問題,我幾乎可以肯定他們中的大多數都會無所適從或者犯下一些錯誤,例如在錯誤的地方使用
synchronized 關鍵詞,沒有對正確的對象使用wait,或者沒有遵循規范的代碼方法。說實話,這個問題對於不常使用它們的程序員來說確實令人感覺比較頭疼。
第一個問題就是,我們怎麼在代碼里使用wait()呢?因為wait()並不是Thread類下的函數,我們並不能使用
Thread.call()。事實上很多Java程序員都喜歡這么寫,因為它們習慣了使用Thread.sleep(),所以他們會試圖使用wait()
來達成相同的目的,但很快他們就會發現這並不能順利解決問題。正確的方法是對在多線程間共享的那個Object來使用wait。在生產者消費者問題中,這
個共享的Object就是那個緩沖區隊列。
第二個問題是,既然我們應該在synchronized的函數或是對象里調用wait,那哪個對象應該被synchronized呢?答案是,那個
你希望上鎖的對象就應該被synchronized,即那個在多個線程間被共享的對象。在生產者消費者問題中,應該被synchronized的就是那個
緩沖區隊列。(我覺得這里是英文原文有問題……本來那個句末就不應該是問號不然不太通……)
永遠在循環(loop)里調用 wait 和 notify,不是在 If 語句
現在你知道wait應該永遠在被synchronized的背景下和那個被多線程共享的對象上調用,下一個一定要記住的問題就是,你應該永遠在
while循環,而不是if語句中調用wait。因為線程是在某些條件下等待的——在我們的例子里,即「如果緩沖區隊列是滿的話,那麼生產者線程應該等
待」,你可能直覺就會寫一個if語句。但if語句存在一些微妙的小問題,導致即使條件沒被滿足,你的線程你也有可能被錯誤地喚醒。所以如果你不在線程被喚
醒後再次使用while循環檢查喚醒條件是否被滿足,你的程序就有可能會出錯——例如在緩沖區為滿的時候生產者繼續生成數據,或者緩沖區為空的時候消費者
開始小號數據。所以記住,永遠在while循環而不是if語句中使用wait!我會推薦閱讀《Effective Java》,這是關於如何正確使用wait和notify的最好的參考資料。
基於以上認知,下面這個是使用wait和notify函數的規范代碼模板:
// The standard idiom for calling the wait method in Java synchronized (sharedObject) { while (condition) { sharedObject.wait(); // (Releases lock, and reacquires on wakeup) } // do action based upon condition e.g. take or put into queue }
就像我之前說的一樣,在while循環里使用wait的目的,是在線程被喚醒的前後都持續檢查條件是否被滿足。如果條件並未改變,wait被調用之前notify的喚醒通知就來了,那麼這個線程並不能保證被喚醒,有可能會導致死鎖問題。
Java wait(), notify(), notifyAll() 範例
下面我們提供一個使用wait和notify的范常式序。在這個程序里,我們使用了上文所述的一些代碼規范。我們有兩個線程,分別名為
PRODUCER(生產者)和CONSUMER(消費者),他們分別繼承了了Procer和Consumer類,而Procer和
Consumer都繼承了Thread類。Procer和Consumer想要實現的代碼邏輯都在run()函數內。Main線程開始了生產者和消費
者線程,並聲明了一個LinkedList作為緩沖區隊列(在Java中,LinkedList實現了隊列的介面)。生產者在無限循環中持續往
LinkedList里插入隨機整數直到LinkedList滿。我們在while(queue.size ==
maxSize)循環語句中檢查這個條件。請注意到我們在做這個檢查條件之前已經在隊列對象上使用了synchronized關鍵詞,因而其它線程不能在
我們檢查條件時改變這個隊列。如果隊列滿了,那麼PRODUCER線程會在CONSUMER線程消耗掉隊列里的任意一個整數,並用notify來通知
PRODUCER線程之前持續等待。在我們的例子中,wait和notify都是使用在同一個共享對象上的。