java - threads - ¿Por qué las InterruptedExceptions borran el estado interrumpido de un hilo?
multithreading python (4)
Escribe tu código así y no necesitarás una bandera:
try {
while (!this._workerThread.isInterrupted()) {
// do something
synchronized (this) {
this.wait(this._waitPeriod);
}
// do something else
}
} catch (InterruptedException e) {
// ignore ...
}
Como @Boyan señala, es una mala idea aplastar la excepción de interrupción ... en general. En este caso, el contexto determinará si debe aplastarlo (como arriba), establecer el indicador de interrupción (nuevamente) o permitir que la excepción se propague. Entre otras cosas, depende de lo que signifique la interrupción en / a su aplicación.
Si un hilo se interrumpe mientras se encuentra dentro de Object.wait()
o Thread.join()
, lanza una InterruptedException
, que restablece el estado interrumpido del hilo . I. e., Si tengo un bucle como este dentro de un Runnable.run()
:
while (!this._workerThread.isInterrupted()) {
// do something
try {
synchronized (this) {
this.wait(this._waitPeriod);
}
} catch (InterruptedException e) {
if (!this._isStopping()) {
this._handleFault(e);
}
}
}
el hilo continuará ejecutándose después de llamar a interrupt()
. Esto significa que tengo que salir explícitamente del bucle al verificar mi propio indicador de parada en la condición de bucle, volver a emitir la excepción o agregar una break
.
Ahora, esto no es exactamente un problema, ya que este comportamiento está bien documentado y no me impide hacer nada de la manera que quiero. Sin embargo, parece que no entiendo el concepto subyacente: ¿por qué no se considera que un hilo ya no se interrumpe una vez que se lanza la excepción? También ocurre un comportamiento similar si obtienes el estado interrumpido con interrupted()
lugar de isInterrupted()
, entonces, también, el hilo solo aparecerá interrumpido una vez.
¿Estoy haciendo algo inusual aquí? Por ejemplo, ¿es más común detectar la InterruptedException
fuera del bucle?
(Aunque no soy exactamente un principiante, etiqueté a este "principiante", porque me parece una pregunta muy básica, mirándolo).
Esto se debe a que una InterruptedException
se considera un evento anormal en el que otra persona intenta detener un hilo desde fuera de él.
Cuando realmente quieres interrumpir un hilo, simplemente rompes su condición de bucle configurando un valor booleano o algo similar. O usas .wait()
y .notify()
desde dentro de ese hilo . Pero si estás haciendo wait()
externamente:
- se emite una excepción para notificar que un subproceso externo intentó interrumpirme o hacerme esperar
- El hilo continúa su trabajo porque no toma ningún orden de otro hilo. Pero el aumento de la excepción le permite agregar un manejo especial y hacer lo que quiera, también detener el hilo de manera efectiva.
La idea es que una interrupción debe manejarse una vez. Si una InterruptedException
explícita no borró el indicador de "interrupción", la mayoría de los receptores de InterruptedException
tendrían que borrar explícitamente ese indicador. A la inversa, puede "no aclarar" el indicador mediante autointerrupción ( Thread.currentThread().interrupt()
). Los diseñadores de Java optaron por la semántica que ahorraría las pulsaciones de teclado la mayor parte del tiempo (es decir, más a menudo desea borrar la bandera que mantenerla configurada).
No debería. Este es un defecto de diseño desafortunado que hace que confiar en las interrupciones sea un negocio arriesgado, ya que con demasiada frecuencia el código de la biblioteca detectará la InterruptedException
sin restablecer la marca de interrupción del hilo y continuar. Si sucede que indica que hay una interrupción en el hilo cuando se está ejecutando ese trozo particular de código de biblioteca, cuando su código recupera el control de ejecución, quedará sin la menor idea de que se produjo la interrupción.
Esto solo debe suceder una vez en cualquier lugar al que esté llamando desde su código, por lo que para poder interrumpir un hilo y luego usar el bit interrumpido para controlar su flujo desde dentro de dicho hilo de manera segura, debe estar al 100%. Asegúrese de que cada pieza de código que está llamando no borre el bit interrumpido por error. Esto es muy difícil de hacer cuando hay bibliotecas involucradas, pero incluso si pudiera tener en cuenta cada una de las bibliotecas que está usando en su código, eso no tiene en cuenta el código JRE con errores que puede cometer el mismo error .
El hecho de que solo se requiera un autor de biblioteca (o JRE!) Para que no le importe o piense en las interrupciones para romper la lógica del código que requiere que se muestre que esta es la acción predeterminada incorrecta. Alguien a quien no le importa el bit interrumpido del hilo probablemente no se molestará en restablecerlo después de detectar InterruptedException
, ¡tal vez ni siquiera sepan que existe! Si la captura de InterruptedException
no restablece el estado interrumpido del hilo, cualquier persona que no sepa sobre el bit interrumpido automáticamente "hará lo correcto" y no causará un problema por ningún código de llamada que dependa de las interrupciones. Cualquier persona que requiera su eliminación podría hacerlo manualmente, pero entonces sería una acción explícita que es mucho más probable que sea correcta que un efecto secundario generalmente no deseado de capturar la excepción InterruptedException
verificada. Tal como está ahora, si confía en el bit interrumpido del hilo, cualquiera que esté en la pila de llamadas que llame a Thread.sleep()
cuidado puede arruinar su día.
Como resultado, la mayoría del código de subprocesos múltiples de Java simplemente duplicará el modelo de interrupción de subprocesos de Java con un campo de instancia "isRunning" y algún mecanismo para voltearlo como solución alternativa.