2009-12-24 11 views
6

Supponiamo di avere un thread che consuma oggetti prodotti da un altro thread. Il suo metodo di esecuzione è il seguente, con INQUEUE essendo un BlockingQueueIn quali condizioni BlockingQueue.take genera un'eccezione interrotta?

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

Inoltre, un thread diverso segnalerà che non ci sono altri elementi di lavoro di interrompere il filo conduttore.() Genera un'eccezione interrotta se non ha bisogno di bloccare per recuperare il successivo oggetto di lavoro. Ad esempio, se il produttore segnala di aver completato la coda di lavoro, è possibile lasciare accidentalmente alcuni elementi in InQueue o saltare l'interruzione?

+1

Hai quasi capito bene. Anziché avere il * consumer * impostato su "shutdown" su true al momento dell'interruzione, invece il * producer * lo imposta su true prima di interrompere l'utente. Nota questo A) mantiene le cose belle evitando un valore sentinella ("poison pill"), B) gestisce correttamente wakeups spuri, e C) è più generale in quanto puoi deliberatamente fermare il consumatore indipendentemente dal fatto che la coda sia vuota. – user359996

risposta

4

Un buon modo per segnalare la chiusura di una coda di blocco è di inviare un valore 'veleno' nella coda che indica che si è verificato un arresto. Ciò garantisce che il comportamento previsto della coda sia onorato. Chiamare Thread.interupt() non è probabilmente una buona idea se si vuole liberare la coda.

Per fornire qualche codice:

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

Questo sembra funzionare meglio. Da quello che posso dire in rari casi, l'interrupt 'done' può essere consegnato prima che take() si riattivi a causa del fatto che ci sono più cose in coda. Per evitare questo, ho dovuto drenare la coda con un secondo ciclo. – Ryan

3

Secondo javadoc, il metodo take() getteranno InterruptedException se interrotto durante l'attesa.

+0

Cosa significa esattamente? "" se interrotto durante l'attesa "" MODIFICA: non importa. Ho capito dalla risposta di FkYkko qui sotto. – WarLord

-1

Il pacchetto java.concurrency.utils è stato progettato e implementato da alcune delle menti più raffinate nella programmazione simultanea. Inoltre, interrompere i thread come mezzo per terminarli viene esplicitamente approvato dal loro libro "Java Concurrency in Practice". Pertanto, sarei estremamente sorpreso se alcuni elementi fossero lasciati in coda a causa di un'interruzione.

+3

Provalo in una prova ed essere "estremamente sorpreso" ;-) – FkYkko

+0

"Le persone intelligenti lo hanno reso" più "interrupt interrupt() i thread" non implicano "le code di blocco non controllano lo stato di interruzione finché non sono vuote". Ad esempio, almeno un'implementazione (vedere http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/ArrayBlockingQueue.java#ArrayBlockingQueue. take) di ArrayBlockingQueue chiama ReentrantLock.lockInterruptibly(), che controlla lo stato di interruzione del thread (lanciando un InterruptedException se interrotto) prima di provare a ottenere l'elemento successivo. – user359996

2

Mi chiedevo la stessa cosa e leggendo la javadoc per take() Credevo che avrebbe gettato un'eccezione interrotta solo dopo aver preso tutti gli elementi in coda, poiché se la coda avesse degli elementi, non avrebbe dovuto "aspettare" ". ma ho fatto un piccolo test:

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()); 
     } 
    } 
} 

} 

Il secondo classificato avrà 10-11 articoli e poi finire cioè prendere() getterà InterruptedException anche se c'è ancora elementi nella coda.

Riepilogo: utilizzare l'approccio Poison pill anziché, in tal caso, il pieno controllo su quanto rimane nella coda.

Problemi correlati