2011-08-11 11 views
5

Estoy tratando de comprender cómo funcionan los subprocesos en Java y actualmente estoy investigando cómo implementar subprocesos en bucle que se pueden cancelar. Aquí está el código:Interrumpir subprocesos enlazados en Java

public static void main(String[] args) throws Exception { 
    Thread t = new Thread() { 
     @Override 
     public void run() { 
      System.out.println("THREAD: started"); 
      try { 
       while(!isInterrupted()) { 
        System.out.printf("THREAD: working...\n"); 
        Thread.sleep(100); 
       } 
      } catch(InterruptedException e) { 
       // we're interrupted on Thread.sleep(), ok 

       // EDIT 
       interrupt(); 

      } finally { 
       // we've either finished normally 
       // or got an InterruptedException on call to Thread.sleep() 
       // or finished because of isInterrupted() flag 

       // clean-up and we're done 
       System.out.println("THREAD: done"); 
      }    
     } 
    }; 

    t.start(); 
    Thread.sleep(500); 
    System.out.println("CALLER: asking to stop"); 
    t.interrupt(); 
    t.join(); 
    System.out.println("CALLER: thread finished"); 
} 

El hilo creo es indentado a ser interrumpido antes o después. Entonces, verifico el indicador Interrumpido() para decidir si necesito continuar y también tomar InterruptedException para manejar casos cuando estoy en una especie de operación de espera (sleep, join, wait).

cosas que me gustaría aclarar son:

  1. Es bien usar mecanismo de interrupción para este tipo de tarea? (en comparación con volatile boolean shouldStop)
  2. ¿Es correcta esta solución?
  3. ¿Es normal que trague InterruptedException? Realmente no me interesa cuál era el fragmento de código en el que alguien me pidió que interrumpiera mi hilo.
  4. ¿Hay formas más breves de resolver este problema? (El punto principal es tener bucle 'infinito')

EDITAR Agregado llamada a interrupt() de captura para InterruptedException.

+2

Está bien, pero es mejor no extender el hilo ya que no extiende su funcionalidad. Implementar un Runnable para ser utilizado por un Thread estándar. –

Respuesta

4

Estoy respondiendo no. 3:

Básicamente la pregunta es: ¿Qué propósito tiene una excepción interrumpida? Te dice que dejes de bloquear (por ejemplo, dormir) y que regreses temprano.

Hay dos maneras que se ocupan de una InterruptedException:

  • volver a lanzar, así que el hilo queda interrumpido
  • conjunto Thread.currentThread.interrupt() nuevo y hacer el trabajo de limpieza. De esta manera usted puede estar seguro de que otro método en su comienzo de la rosca del sueño va a tirar de nuevo

simplemente tragar una InterruptedException no es una buena idea con respecto a los fines de una interrupción de este tipo que es dar por terminado finalmente. Pero solo se le pide que interrumpa para que todavía tenga tiempo para limpiar.

En este caso, esto podría ser una "reacción exagerada" de mí mismo, pero normalmente ese código es mucho más complicado y ¿cómo sabes que algún código de seguimiento en este hilo no volvería a llamar a un método de bloqueo?

EDITAR

de lo contrario creo lo que está haciendo está bien. Para mí fue un poco sorprendente, sin embargo, porque nunca vi a nadie en su propio código que realmente lo hiciera.

e interesante artículo que explica por qué se puede encontrar aquí: http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html

+0

Bueno, creo que volver a lanzar no es un caso, ya que no puedo lanzar desde 'run()' de thread. ¿Debo llamar 'interrupt()'? – agibalov

+0

isInterrupted no restablece el indicador interrumpido. ¿Y cómo se detiene un hilo esperando o durmiendo por mucho tiempo sin interrumpirlo? –

+0

Tienes razón. interrumpido() está restableciendo ... –

2
  1. Sí, está bien. Debe documentar cómo debe detenerse un subproceso/ejecutable.Puede agregar un método de detención dedicado en su implementación de Runnable que encapsula el mecanismo de detención. O utilice la interrupción, o use un valor booleano dedicado, o ambos.
  2. Sí, salvo la buena práctica es restaurar el estado de alarma cuando la captura de InterruptedException: Thread.currentThread().interrupt();
  3. No, usted debe restaurar el estado de alarma
  4. Ninguna que yo sepa
1

1) La En su ejemplo, es preferible utilizar una bandera volátil (que es redundante ya que ya tiene la bandera interrumpida), según el Java Concurrency in Practice libro. Así es como se pretendía utilizar InterruptedExceptions.

2) Sí

3) se puede comer la excepción, siempre y cuando se restaura el estado de la bandera de interrupción. La excepción no representa un error, así que comerlo no pierde ninguna información, es puramente un medio de transferir el control. (Restaurar el estado del indicador de interrupción es importante para los casos en que haya anidado estructuras de control que necesitan que se les informe que el hilo se está cancelando; para un ejemplo simple como el suyo, es bueno, pero si falta, no dañará nada).

4) no

1

Está bien utilizar interrupción, pero úsalas bien. Tienes que volver a lanzar Thread.currentThread().interrupt() en tu captura. Aquí es una pieza de código que muestra por qué:

public class MyThread extends Thread { 
    private static boolean correct = true; 

    @Override 
    public void run() { 
     while (true) { 
      // Do Something 1 
      for (int i = 0; i < 10; i++) { // combined loop 
       // Do Something 2 
       try { 
        Thread.sleep(100); 
       } catch (InterruptedException ex) { 
        if (correct) 
         Thread.currentThread().interrupt(); // reinterrupting 
        System.out.println("First Catch"); 
        break; // for 
       } 
      } 
      try { 
       // Do Something 3 
       System.out.print("before sleep, "); 
       Thread.sleep(1000); 
       System.out.print("After sleep, "); 
      } catch (InterruptedException ex) { 
       if (correct) 
        Thread.currentThread().interrupt(); 
       System.out.println("Second catch"); 
       break; // while 
      } 
     } 
     System.out.println("Thread closing"); 
    } 

    private static void test() throws InterruptedException { 
     Thread t = new MyThread(); 
     t.start(); 
     Thread.sleep(2500); 
     t.interrupt(); 
     t.join(); 
     System.out.println("End of Thread"); 
    } 

    public static void main(String[] args) 
      throws InterruptedException { 
     test(); 
     correct = false; // test "bad" way 
     test(); 
    } 
} 

Otra cosa es, Interruptions no siempre funcionan cuando se espera en InputStreams. Luego puede usar (para algunos) InterruptedIOException, pero no siempre funcionará. Para entender estos casos, es posible que desee probar este fragmento de código:

public class Mythread extends Thread { 
    private InputStream in; 

    public Mythread(InputStream in) { 
     this.in = in; 
    } 

    @Override 
    public void interrupt() { 
     super.interrupt(); 
     try { 
      in.close(); // Close stream if case interruption didn't work 
     } catch (IOException e) {} 
    } 

    @Override 
    public void run() { 
     try { 
      System.out.println("Before read"); 
      in.read(); 
      System.out.println("After read"); 
     } catch (InterruptedIOException e) { // Interruption correctly handled 
      Thread.currentThread().interrupt(); 
      System.out.println("Interrupted with InterruptedIOException"); 
     } catch (IOException e) { 
      if (!isInterrupted()) { // Exception not coming from Interruption 
       e.printStackTrace(); 
      } else { // Thread interrupted but InterruptedIOException wasn't handled for this stream 
       System.out.println("Interrupted"); 
      } 
     } 
    } 

    public static void test1() // Test with socket 
      throws IOException, InterruptedException { 
     ServerSocket ss = new ServerSocket(4444); 
     Socket socket = new Socket("localhost", 4444); 
     Thread t = new Mythread(socket.getInputStream()); 
     t.start(); 
     Thread.sleep(1000); 
     t.interrupt(); 
     t.join(); 
    } 

    public static void test2() // Test with PipedOutputStream 
      throws IOException, InterruptedException { 
     PipedInputStream in = new PipedInputStream(new PipedOutputStream()); 
     Thread t = new Mythread(in); 
     t.start(); 
     Thread.sleep(1000); 
     t.interrupt(); 
     t.join(); 
    } 

    public static void main(String[] args) throws IOException, InterruptedException { 
     test1(); 
     test2(); 
    } 
} 
Cuestiones relacionadas