日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
如何在Java中正確使用wait,notify和notifyAll

wait, notify 和 notifyAll,這些在多線程中被經(jīng)常用到的保留關(guān)鍵字,在實際開發(fā)的時候很多時候卻并沒有被大家重視。本文對這些關(guān)鍵字的使用進行了描述。

公司主營業(yè)務(wù):做網(wǎng)站、成都做網(wǎng)站、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)推出讓胡路免費做網(wǎng)站回饋大家。

在 Java 中可以用 wait、notify 和 notifyAll 來實現(xiàn)線程間的通信。舉個例子,如果你的Java程序中有兩個線程——即生產(chǎn)者和消費者,那么生產(chǎn)者可以通知消費者,讓消費者開始消耗數(shù)據(jù),因為隊列緩 沖區(qū)中有內(nèi)容待消費(不為空)。相應(yīng)的,消費者可以通知生產(chǎn)者可以開始生成更多的數(shù)據(jù),因為當它消耗掉某些數(shù)據(jù)后緩沖區(qū)不再為滿。

我們可以利用wait()來讓一個線程在某些條件下暫停運行。例如,在生產(chǎn)者消費者模型中,生產(chǎn)者線程在緩沖區(qū)為滿的時候,消費者在緩沖區(qū)為空的時 候,都應(yīng)該暫停運行。如果某些線程在等待某些條件觸發(fā),那當那些條件為真時,你可以用 notify 和 notifyAll 來通知那些等待中的線程重新開始運行。不同之處在于,notify 僅僅通知一個線程,并且我們不知道哪個線程會收到通知,然而 notifyAll 會通知所有等待中的線程。換言之,如果只有一個線程在等待一個信號燈,notify和notifyAll都會通知到這個線程。但如果多個線程在等待這個信 號燈,那么notify只會通知到其中一個,而其它線程并不會收到任何通知,而notifyAll會喚醒所有等待中的線程。

在這篇文章中你將會學(xué)到如何使用 wait、notify 和 notifyAll 來實現(xiàn)線程間的通信,從而解決生產(chǎn)者消費者問題。如果你想要更深入地學(xué)習(xí)Java中的多線程同步問題,我強烈推薦閱讀Brian Goetz所著的《Java Concurrency in Practice | Java 并發(fā)實踐》,不讀這本書你的 Java 多線程征程就不完整哦!這是我最向Java開發(fā)者推薦的書之一。

如何使用Wait

盡管關(guān)于wait和notify的概念很基礎(chǔ),它們也都是Object類的函數(shù),但用它們來寫代碼卻并不簡單。如果你在面試中讓應(yīng)聘者來手寫代碼, 用wait和notify解決生產(chǎn)者消費者問題,我?guī)缀蹩梢钥隙ㄋ麄冎械拇蠖鄶?shù)都會無所適從或者犯下一些錯誤,例如在錯誤的地方使用 synchronized 關(guān)鍵詞,沒有對正確的對象使用wait,或者沒有遵循規(guī)范的代碼方法。說實話,這個問題對于不常使用它們的程序員來說確實令人感覺比較頭疼。

***個問題就是,我們怎么在代碼里使用wait()呢?因為wait()并不是Thread類下的函數(shù),我們并不能使用 Thread.call()。事實上很多Java程序員都喜歡這么寫,因為它們習(xí)慣了使用Thread.sleep(),所以他們會試圖使用wait() 來達成相同的目的,但很快他們就會發(fā)現(xiàn)這并不能順利解決問題。正確的方法是對在多線程間共享的那個Object來使用wait。在生產(chǎn)者消費者問題中,這 個共享的Object就是那個緩沖區(qū)隊列。

第二個問題是,既然我們應(yīng)該在synchronized的函數(shù)或是對象里調(diào)用wait,那哪個對象應(yīng)該被synchronized呢?答案是,那個 你希望上鎖的對象就應(yīng)該被synchronized,即那個在多個線程間被共享的對象。在生產(chǎn)者消費者問題中,應(yīng)該被synchronized的就是那個 緩沖區(qū)隊列。(我覺得這里是英文原文有問題……本來那個句末就不應(yīng)該是問號不然不太通……)

永遠在循環(huán)(loop)里調(diào)用 wait 和 notify,不是在 If 語句

現(xiàn)在你知道wait應(yīng)該永遠在被synchronized的背景下和那個被多線程共享的對象上調(diào)用,下一個一定要記住的問題就是,你應(yīng)該永遠在 while循環(huán),而不是if語句中調(diào)用wait。因為線程是在某些條件下等待的——在我們的例子里,即“如果緩沖區(qū)隊列是滿的話,那么生產(chǎn)者線程應(yīng)該等 待”,你可能直覺就會寫一個if語句。但if語句存在一些微妙的小問題,導(dǎo)致即使條件沒被滿足,你的線程你也有可能被錯誤地喚醒。所以如果你不在線程被喚 醒后再次使用while循環(huán)檢查喚醒條件是否被滿足,你的程序就有可能會出錯——例如在緩沖區(qū)為滿的時候生產(chǎn)者繼續(xù)生成數(shù)據(jù),或者緩沖區(qū)為空的時候消費者 開始小號數(shù)據(jù)。所以記住,永遠在while循環(huán)而不是if語句中使用wait!我會推薦閱讀《Effective Java》,這是關(guān)于如何正確使用wait和notify的***的參考資料。

基于以上認知,下面這個是使用wait和notify函數(shù)的規(guī)范代碼模板:

 
 
  1. // The standard idiom for calling the wait method in Java 
  2. synchronized (sharedObject) { 
  3.     while (condition) { 
  4. sharedObject.wait(); 
  5.         // (Releases lock, and reacquires on wakeup) 
  6.     } 
  7.     // do action based upon condition e.g. take or put into queue 

就像我之前說的一樣,在while循環(huán)里使用wait的目的,是在線程被喚醒的前后都持續(xù)檢查條件是否被滿足。如果條件并未改變,wait被調(diào)用之前notify的喚醒通知就來了,那么這個線程并不能保證被喚醒,有可能會導(dǎo)致死鎖問題。

Java wait(), notify(), notifyAll() 范例

下面我們提供一個使用wait和notify的范例程序。在這個程序里,我們使用了上文所述的一些代碼規(guī)范。我們有兩個線程,分別名為 PRODUCER(生產(chǎn)者)和CONSUMER(消費者),他們分別繼承了了Producer和Consumer類,而Producer和 Consumer都繼承了Thread類。Producer和Consumer想要實現(xiàn)的代碼邏輯都在run()函數(shù)內(nèi)。Main線程開始了生產(chǎn)者和消費 者線程,并聲明了一個LinkedList作為緩沖區(qū)隊列(在Java中,LinkedList實現(xiàn)了隊列的接口)。生產(chǎn)者在***循環(huán)中持續(xù)往 LinkedList里插入隨機整數(shù)直到LinkedList滿。我們在while(queue.size == maxSize)循環(huán)語句中檢查這個條件。請注意到我們在做這個檢查條件之前已經(jīng)在隊列對象上使用了synchronized關(guān)鍵詞,因而其它線程不能在 我們檢查條件時改變這個隊列。如果隊列滿了,那么PRODUCER線程會在CONSUMER線程消耗掉隊列里的任意一個整數(shù),并用notify來通知 PRODUCER線程之前持續(xù)等待。在我們的例子中,wait和notify都是使用在同一個共享對象上的。

 
 
  1. import java.util.LinkedList; 
  2. import java.util.Queue; 
  3. import java.util.Random; 
  4. /** 
  5. * Simple Java program to demonstrate How to use wait, notify and notifyAll() 
  6. * method in Java by solving producer consumer problem. 
  7. * @author Javin Paul 
  8. */ 
  9. public class ProducerConsumerInJava { 
  10. public static void main(String args[]) { 
  11.   System.out.println("How to use wait and notify method in Java"); 
  12.   System.out.println("Solving Producer Consumper Problem"); 
  13.   Queue<Integer> buffer = new LinkedList<>(); 
  14.   int maxSize = 10; 
  15.   Thread producer = new Producer(buffer, maxSize, "PRODUCER"); 
  16.   Thread consumer = new Consumer(buffer, maxSize, "CONSUMER"); 
  17.   producer.start(); consumer.start(); } 
  18. /** 
  19. * Producer Thread will keep producing values for Consumer 
  20. * to consumer. It will use wait() method when Queue is full 
  21. * and use notify() method to send notification to Consumer 
  22. * Thread. 
  23. * @author WINDOWS 8 
  24. */ 
  25. class Producer extends Thread 
  26. { private Queue<Integer> queue; 
  27.   private int maxSize; 
  28.   public Producer(Queue<Integer> queue, int maxSize, String name){ 
  29.    super(name); this.queue = queue; this.maxSize = maxSize; 
  30.   } 
  31.   @Override public void run() 
  32.   { 
  33.    while (true) 
  34.     { 
  35.      synchronized (queue) { 
  36.       while (queue.size() == maxSize) { 
  37.        try { 
  38.         System.out .println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue"); 
  39.         queue.wait(); 
  40.        } catch (Exception ex) { 
  41.         ex.printStackTrace(); } 
  42.        } 
  43.        Random random = new Random(); 
  44.        int i = random.nextInt(); 
  45.        System.out.println("Producing value : " + i); queue.add(i); queue.notifyAll(); 
  46.       } 
  47.      } 
  48.     } 
  49.    } 
  50. /** 
  51. * Consumer Thread will consumer values form shared queue. 
  52. * It will also use wait() method to wait if queue is 
  53. * empty. It will also use notify method to send 
  54. * notification to producer thread after consuming values 
  55. * from queue. 
  56. * @author WINDOWS 8 
  57. */ 
  58. class Consumer extends Thread { 
  59.   private Queue<Integer> queue; 
  60.   private int maxSize; 
  61.   public Consumer(Queue<Integer> queue, int maxSize, String name){ 
  62.    super(name); 
  63.    this.queue = queue; 
  64.    this.maxSize = maxSize; 
  65.   } 
  66.   @Override public void run() { 
  67.    while (true) { 
  68.     synchronized (queue) { 
  69.      while (queue.isEmpty()) { 
  70.       System.out.println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue"); 
  71.       try { 
  72.        queue.wait(); 
  73.       } catch (Exception ex) { 
  74.        ex.printStackTrace(); 
  75.       } 
  76.      } 
  77.      System.out.println("Consuming value : " + queue.remove()); queue.notifyAll(); 
  78.     } 
  79.    } 
  80.   } 

為了更好地理解這個程序,我建議你在debug模式里跑這個程序。一旦你在debug模式下啟動程序,它會停止在PRODUCER或者 CONSUMER線程上,取決于哪個線程占據(jù)了CPU。因為兩個線程都有wait()的條件,它們一定會停止,然后你就可以跑這個程序然后看發(fā)生什么了 (很有可能它就會輸出我們以上展示的內(nèi)容)。你也可以使用Eclipse里的Step into和Step over按鈕來更好地理解多線程間發(fā)生的事情。

本文重點:

1. 你可以使用wait和notify函數(shù)來實現(xiàn)線程間通信。你可以用它們來實現(xiàn)多線程(>3)之間的通信。

2. 永遠在synchronized的函數(shù)或?qū)ο罄锸褂脀ait、notify和notifyAll,不然Java虛擬機會生成 IllegalMonitorStateException。

3. 永遠在while循環(huán)里而不是if語句下使用wait。這樣,循環(huán)會在線程睡眠前后都檢查wait的條件,并在條件實際上并未改變的情況下處理喚醒通知。

4. 永遠在多線程間共享的對象(在生產(chǎn)者消費者模型里即緩沖區(qū)隊列)上使用wait。

5. 基于前文提及的理由,更傾向用 notifyAll(),而不是 notify()。

這是關(guān)于Java里如何使用wait, notify和notifyAll的所有重點啦。你應(yīng)該只在你知道自己要做什么的情況下使用這些函數(shù),不然Java里還有很多其它的用來解決同步問題的方 案。例如,如果你想使用生產(chǎn)者消費者模型的話,你也可以使用BlockingQueue,它會幫你處理所有的線程安全問題和流程控制。如果你想要某一個線 程等待另一個線程做出反饋再繼續(xù)運行,你也可以使用CycliBarrier或者CountDownLatch。如果你只是想保護某一個資源的話,你也可 以使用Semaphore。


分享名稱:如何在Java中正確使用wait,notify和notifyAll
當前鏈接:http://m.5511xx.com/article/dhsegpd.html