2014-07-16 40 views
1

我正在研究BlockingQueue接口,其中ArrayBlockingQueue是一個實現。用於演示的目的,我有下發展下面的代碼:ArrayBlockingQueue顯示已被執行的行爲

import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 

public class MainJava { 
    public static void main(String[] args) { 
     BlockingQueue<String> queue = new ArrayBlockingQueue<>(1); 
     Producer producer = new Producer(queue); 
     Consumer consumer = new Consumer(queue); 
     new Thread(producer).start(); 
     new Thread(consumer).start(); 
    } 
} 

class Producer implements Runnable { 
    BlockingQueue<String> queue = null; 

    public Producer(BlockingQueue<String> queue) { 
     this.queue = queue; 
     // TODO Auto-generated constructor stub 
    } 

    @Override 
    public void run() { 
     for (int i = 0; i < 10; i++) { 
      try { 
       System.out.println("Producer added " + i); 
       queue.put(String.valueOf(i)); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

class Consumer implements Runnable { 
    BlockingQueue<String> queue = null; 

    public Consumer(BlockingQueue<String> queue) { 
     this.queue = queue; 
    } 

    @Override 
    public void run() { 
     for (int i = 0; i < 10; i++) { 
      try { 
       System.out.println("Consumer used " + queue.take()); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

預期的行爲

Producer add 0 
Consumer used 0 
Producer add 1 
Consumer used 1 
and so on.. 

Actaul O/P

Producer added 0 
Producer added 1 
Consumer used 0 
Consumer used 1 
Producer added 2 
Producer added 3 
Producer added 4 

我用它工作正常調試模式嘗試。那麼爲什麼沒有調試模式,事情不能正常工作?

+0

這是因爲你的兩個線程彼此並行運行!你無法預測他們的行爲! – ItachiUchiha

+0

@IchichiUchiha實際上他有一個大小爲1的阻塞隊列,所以人們總是在等待。這裏的「問題」是他信任System.out.println的輸出以確定它是否正常工作,這是不可靠的。 – Kayaman

+0

我的兩個線程使用一個隊列,其中一個添加項目,另一個刪除它,當生產者添加一個項目,然後嘗試添加另一個項目時,它應該阻止,當que爲空時,消費者應該被阻止,直到項目被添加到隊列中。對於我們可以使用BlockingQueue的權利? – user3608352

回答

2

你的代碼的工作,因爲它應該 - 它只是一個「假象」,你的隊列,似乎在引起不幸的定時時間到包含多個項目和System.out.println在錯誤的地方:

在你製作,System.out.println出現在阻止put之前,所以即使put必須等到隊列變空時,纔會打印'Producer added x'。

但是,即使我們交換這兩行(具有putSystem.out.println之前推出),輸出似乎是錯誤的:

Consumer used 0 
Producer added 0 
Producer added 1 
Consumer used 1 
Consumer used 2 
Producer added 2 
Producer added 3 
Consumer used 3 

不過,這是由吉利時機的錯覺。正如你所看到的,在我的輸出中,消費者似乎在生產者將其放入隊列之前消耗了元素!這當然不是這種情況。 System.out是一個PrintStream對象,它是線程安全的。所以只有一個線程可以同時打印一些東西。在上面的運行中,消費者線程只是在生產者之前抓住了鎖。

執行的順序可能是類似的東西:

  1. 監製:put 0 into queue
  2. 消費者:take 0 from queue
  3. 消費者:print 'Consumer used 0'
  4. 消費者:wait until queue not empty
  5. 監製:print 'Producer added 0'
  6. 製片人:put 1 into queue
  7. 監製:print 'Producer added 1'
  8. 監製:wait until queue not full
  9. 消費者:take 1 from queue
  10. 消費者:print 'Consumer used 1'
  11. 監製:put 2 into queue
  12. 消費者:take 2 from queue
  13. 消費者:print 'Consumer used 2'
  14. 監製:print 'Producer added 2'

它在多線程系統的爭論正確的行爲非常困難......

2

@Kayaman其實已經回答了這個問題,但也許更多的信息可能會有所幫助在這裏。

編輯...和isnot2bad寫了類似的答案(+1)。我顯然太慢了。不過,也許它被認爲是有用的。

首先:代碼

System.out.println("Producer added " + i); 
queue.put(String.valueOf(i)); 

它實際上添加到隊列前將明顯打印Producer added 1。只需將線交換到

queue.put(String.valueOf(i)); 
System.out.println("Producer added " + i); 

將減少,其中輸出似乎是錯誤的案件數量,但不能完全消除它們。原因是這兩條線不是原子

一個例子時間表,顯示了每個線程的每一步做顯示的結果可能看起來隨意錯,這取決於操作的順序:

Producer thread:   Consumer thread:   Queue:  Output: 
------------------------------------------------------------------------ 
queue.put(0);          [0] 
println("Added " + 0);        []   Added 0 
          String 0 = queue.take(); [] 
          println("Used " + 0);  []   Used 0 
queue.put(1);          [1]   
          String 1 = queue.take(); [] 
println("Added " + 1);        []   Added 1 
queue.put(2);          [2]   
println("Added " + 2);        [2]   Added 2 
          println("Used " + 1);  [2]   Used 1 
          String 2 = queue.take(); [] 
          println("Used " + 2);  []   Used 2 
queue.put(3);          [3]   
          String 3 = queue.take(); [] 
          println("Used " + 3);  []   Used 3 
println("Added " + 3);        []   Added 3 
queue.put(4);          [4]   
println("Added " + 4);        [4]   Added 4 

不過,關鍵的一點是,隊列永遠包含多個元素。

+0

+1尼斯時間表!你甚至可以並行化某些步驟,例如生產者的前6個步驟可以在一個流程(沒有空行)中執行,而消費者在一個塊中執行其前三個步驟(具有一個步驟延遲)。 – isnot2bad