2009-12-24 69 views
6

讓我們假設我有一個消耗另一個線程產生的物品的線程。它的運行方法如下,與INQUEUE是一個BlockingQueue的BlockingQueue.take在什麼情況下會拋出異常中斷?

boolean shutdown = false; 
while (!shutdown) { 
    try { 
     WorkItem w = inQueue.take(); 
     w.consume(); 
    } catch (InterruptedException e) { 
     shutdown = true; 
    } 
} 

此外,不同的線程將發出信號,有通過中斷這個正在運行的線程沒有更多的工作項目。如果不需要阻塞以檢索下一個工作項,將採取()拋出一個被中斷的異常。即如果生產者發出信號表示已完成填充工作隊列,是否可能意外地將一些項目留在隊列中或錯過中斷?

+1

你已經差不多了。而不是讓* consumer *在中斷時將「shutdown」設置爲true,而是讓* producer *在中斷消費者之前將其設置爲true。注意這個A)通過避免一個前哨值(「毒丸」)來保持漂亮,B)正確地處理虛假喚醒,C)更通用,因爲無論隊列是否爲空,你都可以故意停止消費者。 – user359996 2012-08-09 20:59:56

回答

4

一個信號終止阻塞隊列的好方法是向隊列中提交一個'毒'值,指示發生了關閉。這確保了隊列的預期行爲得到遵守。如果你關心清除隊列,調用Thread.interupt()可能不是一個好主意。

提供一些代碼:

boolean shutdown = false; 
while (!shutdown) { 
    try { 
     WorkItem w = inQueue.take(); 
     if (w == QUEUE_IS_DEAD) 
      shutdown = true; 
     else 
      w.consume(); 
    } catch (InterruptedException e) { 
     // possibly submit QUEUE_IS_DEAD to the queue 
    } 
} 
+0

這似乎最適用。根據我在罕見情況下可以看到的'完成'中斷可以在take()喚醒之前交付,因爲更多的東西在隊列中。爲了防止這種情況發生,我不得不用第二個循環排隊。 – Ryan 2009-12-24 20:00:59

3

javadoc,該take()方法將拋出InterruptedException如果等待時中斷。

+0

這究竟意味着什麼? 「」如果在等待時中斷「」 編輯:沒關係。我從下面的FkYkko的回答中瞭解到。 – WarLord 2017-11-08 08:45:01

-1

java.concurrency.utils包是由一些最好的併發編程思想設計和實現的。此外,中斷線程作爲終止它們的手段,明確得到了他們的書「實踐中的Java併發」的贊同。因此,如果有任何項目由於中斷而留在隊列中,我會感到非常驚訝。

+3

在測試中試試它,並且「非常驚訝」;-) – FkYkko 2011-07-08 15:28:00

+0

「聰明的人制作了它」加上「interrupt()中斷線程」並不需要「阻塞隊列不檢查中斷狀態,直到它們爲空」。例如,至少有一個實現(參見http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/ArrayBlockingQueue.java#ArrayBlockingQueue。採取)ArrayBlockingQueue調用ReentrantLock.lockInterruptibly(),它會在嘗試獲取下一個元素之前檢查線程的中斷狀態(如果中斷,則拋出InterruptedException)。 – user359996 2012-08-09 20:56:47

2

我想知道同樣的事情,並閱讀了對於take()的javadoc我相信它只會在獲取隊列中的所有項目後纔會拋出一箇中斷的異常,因爲如果隊列中有項目,就不必「等待」。 但我做了一個小測試:

package se.fkykko.slask; 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.atomic.AtomicLong; 

public class BlockingQueueTakeTest { 

public static void main(String[] args) throws Exception { 
    Runner t = new Runner(); 
    Thread t1 = new Thread(t); 
    for (int i = 0; i < 50; i++) { 
     t.queue.add(i); 
    } 
    System.out.println(("Number of items in queue: " + t.queue.size())); 
    t1.start(); 
    Thread.sleep(1000); 
    t1.interrupt(); 
    t1.join(); 
    System.out.println(("Number of items in queue: " + t.queue.size())); 
    System.out.println(("Joined t1. Finished")); 

} 

private static final class Runner implements Runnable { 
    BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(100); 
    AtomicLong m_count = new AtomicLong(0); 

    @Override 
    public void run() { 
     try { 
      while (true) { 
       queue.take(); 
       System.out.println("Took item " + m_count.incrementAndGet()); 
       final long start = System.currentTimeMillis(); 
       while ((System.currentTimeMillis() - start) < 100) { 
        Thread.yield(); //Spin wait 
       } 
      } 
     } 
     catch (InterruptedException ex) { 
      System.out.println("Interrupted. Count: " + m_count.get()); 
     } 
    } 
} 

} 

亞軍將採取10-11項,然後完成即採取()將拋出InterruptedException即使仍然在隊列中的項目。總結:使用毒丸方法代替,然後你可以完全控制隊列中剩下多少。

相關問題