2009-12-24 24 views
6

Supongamos que tengo un hilo que consume elementos producidos por otro hilo. Su método de ejecución es el siguiente, con INQUEUE ser un BlockingQueue¿En qué condiciones lanzará BlockingQueue.take una excepción interrumpida?

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

Por otra parte, un hilo diferente, señal de que no hay más elementos de trabajo por la interrupción de este hilo conductor. Will take() arroja una excepción interrumpida si no necesita bloquear para recuperar el siguiente elemento de trabajo. es decir, si el productor indica que se ha completado el llenado de la cola de trabajo, ¿es posible dejar accidentalmente algunos elementos en inQueue o perder la interrupción?

+1

Casi lo has acertado. En lugar de hacer que * consumer * establezca "shutdown" en true en caso de interrupción, en su lugar, el * producer * debe establecerlo en true antes de interrumpir al consumidor. Tenga en cuenta que A) mantiene las cosas bonitas al evitar un valor centinela ("píldora venenosa"), B) maneja correctamente los despertadores espurios, y C) es más general en que puede detener deliberadamente al consumidor ya sea que la cola esté vacía o no. – user359996

Respuesta

4

Una buena forma de señalizar la terminación de una cola de bloqueo es enviar un valor 'veneno' en la cola que indica que se ha producido un apagado. Esto asegura que se respeta el comportamiento esperado de la cola. Llamar a Thread.interupt() probablemente no sea una buena idea si te importa borrar la cola.

proporcionar alguna código:

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

Esto parece funcionar mejor. Por lo que puedo decir en casos raros, la interrupción 'finalizada' puede ser entregada antes de que take() se despierte debido a que hay más cosas en la cola. Para protegerse de esto, tuve que drenar la cola con un segundo bucle de todos modos. – Ryan

3

Según javadoc, el método arrojará take()InterruptedException si se interrumpe mientras espera.

+0

¿Qué significa exactamente? "" si se interrumpe mientras se espera "" EDITAR: No importa. Entendí por la respuesta de FkYkko a continuación. – WarLord

-1

El paquete java.concurrency.utils fue diseñado e implementado por algunas de las mejores mentes en la programación concurrente. Además, la interrupción de los hilos como medio para terminarlos está respaldada explícitamente por su libro "Concurrencia de Java en la práctica". Por lo tanto, me sorprendería muchísimo si quedaban elementos en la cola debido a una interrupción.

+3

Pruébalo en una prueba y queda "extremadamente sorprendido" ;-) – FkYkko

+0

"La gente inteligente lo hizo" más "interrupt() interrumpe los hilos" no implica "las colas de bloqueo no verifican el estado de interrupción hasta que están vacías". Por ejemplo, al menos una implementación (consulte http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/ArrayBlockingQueue.java#ArrayBlockingQueue. take) de ArrayBlockingQueue llama a ReentrantLock.lockInterruptibly(), que verifica el estado de interrupción del hilo (lanzando una InterruptedException si se interrumpe) antes de tratar de obtener el siguiente elemento. – user359996

2

Me pregunté sobre lo mismo y leí el javadoc para take(). Creía que lanzaría una excepción interrumpida solo después de haber tomado todos los elementos en la cola, ya que si la cola tuviera elementos, no tendría que "esperar" ". pero he hecho una pequeña prueba:

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

} 

El corredor se llevará a 10-11 artículos y luego terminar es decir, tomar() lanzará InterruptedException incluso si todavía hay elementos de la cola.

Resumen: utilice el método de la píldora venenosa en su lugar, entonces usted tiene control total sobre cuánto queda en la cola.

Cuestiones relacionadas