java multithreading exception-handling thread-safety interrupted-exception

java - ¿Está bien ignorar InterruptedException si nadie llama a interrupt()?



multithreading exception-handling (8)

Creo que ignorar que InterruptedException es seguro o no, depende de lo que estés haciendo en ese hilo. Si hay un escenario donde una interrupción no deseada en su hilo puede dejar el Sistema o cualquier recurso en estado no deseado (sucio, bloqueado, no liberado y que puede causar la fuga de recursos), entonces es su responsabilidad manejar la excepción InterruptedException y no debe ignorarla. Depende de usted si desea que su hilo sea interrumpible o no.

En otras palabras, el mecanismo de interrupción se usa principalmente para implementar las políticas de cancelación y limpieza de tareas. ¿No tiene nada que limpiar en la tarea o no tiene ninguna política específica de cancelación de tareas, ignorar IntrruptedException puede no parecer políticamente correcto, pero está bien?

Si creo mi propio hilo (es decir, no un threadpool) y en alguna parte llamo sleep o cualquier otro método interrumpible, ¿está bien ignorar la InterruptedException si sé que nadie más en el código está haciendo una interrupción en el hilo ?

En otras palabras, si se supone que el hilo debe vivir tanto tiempo como la JVM, lo que significa que el hilo no se puede interrumpir, ¿es seguro suponer que nunca se llamará a InterruptedException y, por lo tanto, la excepción se puede tragar?


En lugar de tragarlo, si estás tan seguro de que nunca sucederá, puedes colgar en lugar de ignorarlo. Por ejemplo, lanzar un Error (o una RuntimeException ):

try { Thread.sleep(1000); } catch (InterruptedException e) { throw new Error(e); }

De los Javadocs para Error :

Un error es una subclase de Throwable que indica problemas graves que una aplicación razonable no debería intentar atrapar. La mayoría de esos errores son condiciones anormales.

Si crees que vale la pena asumir que algo nunca sucederá, entonces si sucede, esa es una "condición anormal" que, quién sabe, podría ser simplemente un "problema serio".

Otra forma de ver esto es, si alguien en el futuro interrumpe su método, ¿hay alguna posibilidad de que quieran que siga funcionando como si nada hubiera pasado? Yo diría que no.


No debe ser ignorado. Debe devolverse a la persona que llama o debe reafirmar el estado interrumpido de la secuencia que se borra cuando se lanza la excepción:

catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); }

a menos que salga prontamente del hilo después de la captura.

Aquí hay un artículo sobre cómo lidiar con InterruptedException s.


Nunca debe tragar una excepción si cree que nunca debería ocurrir. Está bien decidir no agregar un código que maneje una condición que nunca ocurre pero que no debería ocurrir silenciosamente .

Lo mínimo que debes hacer es:

catch(InterruptedException ex) { throw new AssertionError(ex); }

Esto garantiza que cuando su afirmación de que esto nunca ocurrirá sea incorrecta, lo notará. Este patrón también se aplica a otras excepciones inesperadas, por ejemplo, IOException cuando sabes que el OutputStream objetivo es un ByteArrayOutputStream o el Appendable de un Formatter debe ser un StringBuilder , etc.

Por cierto, hay una alternativa a Thread.sleep no requiere tratar con InterruptedException en absoluto:

LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(timeInMillis));

Este método simplemente regresará antes cuando el hilo se haya interrumpido y retendrá el estado interrumpido del Thread por lo que ya proporciona un manejo correcto para el caso de que alguien más use el código (suponiendo que el que llama de su código verifica el estado interrumpido luego).


Si su Projekt crece y se vuelve más complejo, cada vez es más difícil decir que definitivamente nadie llamaría al método de interrupción. Si alguien llamara a este método algún día y se produce una interruptedException, es realmente difícil depurar esto si no está registrando al menos en alguna parte.


El libro que todavía considero la Biblia sobre estos asuntos, Goetz et al. Java Concurrency in Practice dice los siguientes aspectos importantes en el capítulo 7 (Casi el mismo material se encuentra en el artículo de developerWorks de Goetz, que tiene la ventaja de ser gratuito, pero el libro es un poco más claro en algunos puntos).

La interrupción es un mecanismo cooperativo. Un hilo no puede obligar a otro a detener lo que está haciendo y hacer otra cosa; cuando el hilo A interrumpe el hilo B, A simplemente solicita que B detenga lo que está haciendo cuando llega a un punto de parada conveniente, si así lo desea.

[...]

Solo el código que implementa una política de interrupción de subprocesos puede tragarse una solicitud de interrupción. La tarea de propósito general y el código de la biblioteca nunca deben tragar las solicitudes de interrupción.

Entonces, sí, puedes "tragar" InterruptedException en las circunstancias que indicaste.

También un punto importante en el libro:

Debido a que cada hilo tiene su propia política de interrupción, no debes interrumpir un hilo a menos que sepas qué significa la interrupción para ese hilo.

De modo que puede elegir su propia política, como "bloquearse" con una RuntimeException , AssertionError , simplemente iniciar sesión como advertencia, o ignorar la interrupción por completo, siempre que documente este problema para quien quiera que lo necesite. Lo mejor depende de lo que realmente está haciendo su hilo y no ha proporcionado ningún detalle al respecto. Por ejemplo, si se está ejecutando en un cohete [decir haciendo control de actitud], no querrás colgar la JVM / cohete solo porque un compañero programador [que ni siquiera podría trabajar para la misma compañía que tú, por ejemplo, te escribió una biblioteca llamada] se olvidó de un cierto contrato en algún lugar de su código y volcó una excepción segura e ignorable en el suyo: "La excepción que ocurrió no se debió a una falla aleatoria sino a un error de diseño".


Ignorar una excepción marcada nunca se considera seguro.
Puede estar bien para usted en este momento, pero si otro programador extiende su código, esperará el comportamiento estándar: el hilo reacciona a una llamada de interrupción.
También un bloque de captura vacío es peligroso en este caso, ya que la JVM elimina la bandera interrumpida y definitivamente debería establecerse nuevamente con

Thread.currentThread().interrupt();

en el bloque de captura. En mi opinión, esta es la implementación de captura mínima para InterruptedException . La comprobación del indicador isInterrupted en un bucle tampoco duele demasiado.
Es poca sobrecarga en comparación con la dificultad de su futuro programador buscando uno o dos días para el comportamiento de subprocesos inesperados, ya que el proyecto puede haber crecido un poco.

Si siente que la legibilidad de su código sufre de esas implementaciones de captura, puede implementar su propio método de utilidad safeSleep , que se encarga de la Exception y establece la bandera correctamente.

Por otro lado, InterruptedException no es lanzada por la propia JVM en caso de una falla de hardware, sino que es una Exception indicada por el usuario . Entonces, si no propagas la referencia de tu Thread , no habrá ningún otro Thread que pueda invocar a Thread.interrupt() . Eso es técnicamente . Pero no debes subestimar el factor humano y la evolución de tus programas.
Editar: Como señaló el ruakh , en realidad hay una manera de obtener una referencia de Thread y, por lo tanto, programar una llamada a Thread.interrupt() . De esta forma, el desarrollador en celo puede que ni siquiera tenga que mirar la clase, que implementa su Thread ininterrumpible. En mi opinión, esa es otra razón más para implementar un manejo de excepciones adecuado.
Otra cosa : si no está lanzando una Exception , registrar tal evento en un nivel más allá de INFO puede ser una buena opción.


si se supone que el hilo debe vivir tanto tiempo como la JVM, lo que significa que el hilo no se puede interrumpir, ¿es seguro suponer que InterruptedException nunca se lanzará y, por lo tanto, la excepción se puede tragar?

Entiendo tu dolor, la proliferación de bloques repetitivos de prueba sin valor para el negocio daña el código.

Si el código donde intentará tragar la excepción está completamente bajo su control, y si es solo un código de cliente (nunca será reutilizado en un contexto que no sea el suyo), entonces es seguro tragarse la excepción. Tenga en cuenta que hay muchas si hay allí.

Si trabaja con Java 8, tiene otra opción, descrita aquí : envuelva su código dentro de un uncheckCall(() -> { ... }) . Esto permite que el bloque de código arroje la InterruptedException sin declararlo. Como se explica en la respuesta vinculada, esto debe manejarse con extremo cuidado, pero tiene el potencial de hacer que su código esté bastante limpio.