Manejo de excepción interrumpida en Java

Resuelto softwarematter asked hace 14 años • 7 respuestas

¿ Cuál es la diferencia entre las siguientes formas de manipulación InterruptedException? ¿Cuál es la mejor manera de hacerlo?

try {
 // ...
} catch (InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

...o:

try {
 //...
} catch (InterruptedException e) {
   throw new RuntimeException(e);
}

También me gustaría saber en qué escenarios se utilizan estos dos.

softwarematter avatar Oct 20 '10 16:10 softwarematter
Aceptado

¿Cuál es la diferencia entre las siguientes formas de manejar InterruptedException? ¿Cuál es la mejor manera de hacerlo?

Probablemente hayas llegado a hacer esta pregunta porque has llamado a un método que arroja InterruptedException.

En primer lugar, deberías ver throws InterruptedExceptionqué es: una parte de la firma del método y un posible resultado de llamar al método que estás llamando. Así que comience por aceptar el hecho de que an InterruptedExceptiones un resultado perfectamente válido de la llamada al método.

Ahora, si el método al que estás llamando arroja dicha excepción, ¿qué debería hacer tu método? Puedes encontrar la respuesta pensando en lo siguiente:

¿Tiene sentido que el método que está implementando arroje un InterruptedException? Dicho de otra manera, ¿es InterruptedExceptionun resultado sensato al llamar a su método?

  • En caso afirmativo , entonces throws InterruptedExceptiondebería ser parte de la firma de su método y debería dejar que la excepción se propague (es decir, no detectarla en absoluto).

    Ejemplo : su método espera un valor de la red para finalizar el cálculo y devolver un resultado. Si la llamada de red de bloqueo genera un InterruptedExceptionmétodo, su método no puede finalizar el cálculo de forma normal. Dejas que se InterruptedExceptionpropague.

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
    
  • Si no , entonces no deberías declarar tu método con throws InterruptedExceptiony deberías (¡debes!) detectar la excepción. Ahora es importante tener en cuenta dos cosas en esta situación:

    1. Alguien interrumpió tu hilo. Probablemente alguien esté ansioso por cancelar la operación, finalizar el programa con gracia o lo que sea. Debes ser cortés con esa persona y regresar de tu método sin más preámbulos.

    2. Aunque su método puede lograr producir un valor de retorno razonable en caso de InterruptedExceptionque el hilo haya sido interrumpido, aún puede ser importante. En particular, el código que llama a su método puede estar interesado en saber si se produjo una interrupción durante la ejecución de su método. Por lo tanto, debes registrar el hecho de que se produjo una interrupción configurando el indicador de interrupción:Thread.currentThread().interrupt()

    Ejemplo : El usuario ha solicitado imprimir una suma de dos valores. Imprimir " Failed to compute sum" es aceptable si no se puede calcular la suma (y mucho mejor que dejar que el programa falle con un seguimiento de la pila debido a un InterruptedException). En otras palabras, no tiene sentido declarar este método con throws InterruptedException.

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }
    

A estas alturas debería quedar claro que simplemente hacerlo throw new RuntimeException(e)es una mala idea. No es muy cortés con la persona que llama. Podría inventar una nueva excepción de tiempo de ejecución, pero la causa raíz (alguien quiere que el hilo detenga la ejecución) podría perderse.

Otros ejemplos:

ImplementaciónRunnable : Como habrás descubierto, la firma de Runnable.runno permite volver a lanzarlo InterruptedExceptions. Bueno, te registraste para implementar Runnable, lo que significa que te registraste para lidiar con posibles InterruptedExceptions. Elija una interfaz diferente, como Callable, o siga el segundo enfoque anterior.

 

LlamadaThread.sleep : estás intentando leer un archivo y la especificación dice que debes intentarlo 10 veces con 1 segundo entre ellas. Llama Thread.sleep(1000). Entonces, debes lidiar con InterruptedException. Para un método como tryToReadFileeste, tiene mucho sentido decir: "Si me interrumpen, no puedo completar mi acción de intentar leer el archivo" . En otras palabras, tiene mucho sentido que el método arroje InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

Esta publicación ha sido reescrita como un artículo aquí .

aioobe avatar Oct 20 '2010 09:10 aioobe

Da la casualidad de que estaba leyendo sobre esto esta mañana de camino al trabajo en Java Concurrency In Practice de Brian Goetz. Básicamente dice que debes hacer una de tres cosas.

  1. Propagar elInterruptedException - Declare su método para lanzar el marcado InterruptedExceptionpara que la persona que llama tenga que lidiar con él.

  2. Restaurar la interrupción : a veces no se puede lanzar InterruptedException. En estos casos, debe detectar InterruptedExceptiony restaurar el estado de la interrupción llamando al interrupt()método currentThreadpara que el código que se encuentra más arriba en la pila de llamadas pueda ver que se emitió una interrupción y regresar rápidamente del método. Nota: esto sólo es aplicable cuando su método tiene semántica de "intentar" o "mejor esfuerzo", es decir, no sucedería nada crítico si el método no logra su objetivo. Por ejemplo, log()o sendMetric()puede ser tal método, o boolean tryTransferMoney(), pero no void transferMoney(). Consulte aquí para obtener más detalles.

  3. Ignore la interrupción dentro del método, pero restaure el estado al salir , por ejemplo, a través de Guava Uninterruptibles. Uninterruptiblesasumir el código repetitivo como en el ejemplo de Tarea no cancelable en JCIP § 7.1.3.
mR_fr0g avatar Oct 20 '2010 09:10 mR_fr0g

¿Que estás tratando de hacer?

Se InterruptedExceptionlanza cuando un hilo está esperando o inactivo y otro hilo lo interrumpe usando el interruptmétodo de la clase Thread. Entonces, si detecta esta excepción, significa que el hilo ha sido interrumpido. Por lo general, no tiene sentido Thread.currentThread().interrupt();volver a llamar, a menos que desee verificar el estado "interrumpido" del hilo desde otro lugar.

Con respecto a su otra opción de lanzar un RuntimeException, no parece muy inteligente (¿quién se dará cuenta de esto? ¿Cómo se manejará?), pero es difícil decir más sin información adicional.

Grodriguez avatar Oct 20 '2010 09:10 Grodriguez

La opción predeterminada correcta es agregar InterruptedException a su lista de lanzamientos. Una interrupción indica que otro hilo desea que el suyo termine. El motivo de esta solicitud no es evidente y es completamente contextual, por lo que si no tiene ningún conocimiento adicional debe asumir que es solo un cierre amistoso, y cualquier cosa que evite ese cierre es una respuesta no amigable.

Java no arrojará aleatoriamente InterruptedException, todos los consejos no afectarán su aplicación, pero me he topado con un caso en el que el desarrollador que siguió la estrategia de "tragar" se volvió muy inconveniente. Un equipo desarrolló un gran conjunto de pruebas y utilizó mucho Thread.Sleep. Ahora comenzamos a ejecutar las pruebas en nuestro servidor CI y, a veces, debido a defectos en el código, nos quedábamos atascados en esperas permanentes. Para empeorar la situación, al intentar cancelar el trabajo de CI, nunca se cerró porque el Thread.Interrupt que estaba destinado a cancelar la prueba no canceló el trabajo. Tuvimos que iniciar sesión en el cuadro y finalizar manualmente los procesos.

En pocas palabras, si simplemente lanza la InterruptedException, estará coincidiendo con la intención predeterminada de que su hilo debería finalizar. Si no puede agregar InterruptedException a su lista de lanzamientos, lo incluiría en una RuntimeException.

Se puede argumentar muy racionalmente que InterruptedException debería ser una RuntimeException en sí misma, ya que eso fomentaría un mejor manejo "predeterminado". No es una RuntimeException solo porque los diseñadores se apegaron a una regla categórica de que una RuntimeException debería representar un error en su código. Dado que una InterruptedException no surge directamente de un error en su código, no lo es. Pero la realidad es que a menudo surge una InterruptedException porque hay un error en su código (es decir, un bucle sin fin, un punto muerto), y la Interrupción es el método de algún otro hilo para tratar ese error.

Si sabe que es necesario realizar una limpieza racional, hágalo. Si conoce una causa más profunda de la interrupción, puede asumir un manejo más completo.

Entonces, en resumen, sus opciones de manejo deben seguir esta lista:

  1. De forma predeterminada, agregue a los lanzamientos.
  2. Si no se le permite agregar lanzamientos, lanza RuntimeException(e). (La mejor opción entre múltiples opciones malas)
  3. Sólo cuando conozca una causa explícita de la interrupción, maneje como desee. Si su manejo es local para su método, reinicie interrumpido por una llamada a Thread.currentThread().interrupt().
nedruod avatar Sep 20 '2013 21:09 nedruod

Para mí, la clave de esto es: una InterruptedException no significa que nada vaya mal, es el hilo que hace lo que usted le dijo que hiciera. Por lo tanto, volver a lanzarlo envuelto en una RuntimeException no tiene ningún sentido.

En muchos casos, tiene sentido volver a generar una excepción envuelta en una RuntimeException cuando dices: No sé qué salió mal aquí y no puedo hacer nada para solucionarlo, solo quiero que salga del flujo de procesamiento actual. y presione cualquier controlador de excepciones de toda la aplicación que tenga para que pueda registrarlo. Ese no es el caso con una InterruptedException, es solo el subproceso que responde a la llamada de interrupción (), y lanza la InterruptedException para ayudar a cancelar el procesamiento del subproceso de manera oportuna.

Así que propague la InterruptedException, o cómela inteligentemente (es decir, en un lugar donde habrá logrado lo que debía hacer) y restablezca el indicador de interrupción. Tenga en cuenta que el indicador de interrupción se borra cuando se lanza la InterruptedException; la suposición que hacen los desarrolladores de la biblioteca Jdk es que detectar la excepción equivale a manejarla, por lo que, de forma predeterminada, la bandera está borrada.

Definitivamente, la primera forma es mejor, el segundo ejemplo publicado en la pregunta no es útil a menos que no espere que el hilo se interrumpa, e interrumpirlo equivale a un error.

Aquí hay una respuesta que escribí que describe cómo funcionan las interrupciones, con un ejemplo . Puede ver en el código de ejemplo dónde se utiliza InterruptedException para salir de un bucle while en el método de ejecución de Runnable.

Nathan Hughes avatar Aug 03 '2011 07:08 Nathan Hughes