Subproceso de Java: el método de ejecución no puede arrojar una excepción marcada
multithreading exception (6)
En el hilo de Java, el método ''ejecutar'' no puede arrojar una ''excepción marcada''. Me encontré con esto en el libro Core Java (vol 1). ¿Alguien puede explicar el razonamiento detrás de esto?
¿Qué atraparía la excepción y la manejaría? Supongamos que el método de ejecución arroja una excepción marcada. Entonces podrías escribir un código como este:
Thread myThread = new Thread(aRunnable);
try{
myThread.start();
}
catch(Exception e){
e.printStackTrace();
}
//do other stuff
PERO una vez que llama a myThread.start
, el nuevo hilo se inicia en segundo plano y el hilo actual continúa y sale del try-catch y hace otras cosas. Entonces, si myThread
arrojó una excepción más adelante, ¡no puedes atraparla!
Lo que debe hacer es tratar con la excepción dentro del método de run
y luego probablemente tenga una forma de notificar a otro objeto que este subproceso falló.
La razón es que la excepción se devuelve a la persona que llama. El método Caller of run () no es tu código. Es el Thred en sí mismo. Entonces, incluso si run () arroja una excepción, el programa no puede atraparlo.
Deberías poner el resultado de la ejecución del hilo en una variable de nivel de clase y luego leerla desde allí. O, alternativamente, use una nueva API: ejecutores y la interfaz Invocable que declara el método call () que devuelve el resultado futuro de la ejecución de la secuencia.
La solución más obvia a las respuestas anteriores es que si lanza una excepción marcada, no está implementando correctamente run () como se especifica en la interfaz ejecutable.
Ni siquiera compilará
run() in TestClass cannot implement run() in java.lang.Runnable;
overridden method does not throw java.lang.Exception
Supongamos que el subproceso A inicia el subproceso B. Luego, el subproceso B arroja una excepción. Podrías pensar que sería bueno para el hilo A atraparlo. ¿Pero donde? Para cuando el hilo B lanza la excepción, ¿quién sabe qué hilo A está haciendo? Para tomar un ejemplo trivial, supongamos que tenemos este código en el hilo A:
try
{
threadB=new PurgeAbandonedCarts();
threadB.start();
}
catch (NullPointerException panic)
{
... handle errors purging abandoned carts ...
}
try
{
processNewOrders();
}
catch (NullPointerException panic)
{
... handle problems in new orders ...
}
finally
{
... clean up ...
}
Así que iniciamos el hilo B para purgar los carros abandonados. Una vez que se inicia, pasamos al procesamiento de nuevos pedidos. Luego, el hilo B arroja una excepción de puntero nulo. ¿Debería atraparlo el bloque catch asociado con el thread B, o el asociado con el procesamiento de nuevos pedidos?
Si va a la captura de nuevas órdenes, es probable que cualquier código aquí no tenga nada que ver con la eliminación de problemas con el subproceso B. Esa no puede ser la respuesta correcta.
Si dices el que está asociado con el hilo B, significa que durante el procesamiento de los nuevos pedidos, el control podría ser retirado y enviado repentinamente para probar el bloque catch B del hilo. Pero, ¿qué pasó con el procesamiento de nuevas órdenes? ¿Simplemente nos detenemos en el medio? ¿Ni siquiera golpeamos el bloque final? Y cuando terminemos, ¿seguiremos ejecutando y luego volveremos a procesar nuevas órdenes? ¿Procesamos los pedidos dos veces? Esta tampoco puede ser la respuesta correcta.
Por lo tanto, no hay ningún lugar adonde ir si ejecutar arroja una excepción. Lo único lógico que hay que hacer es hacer que el método run capture todas las excepciones lanzadas y manejarlas dentro del nuevo hilo.
throws
declaraciones throws
son parte de la firma de los métodos. Para permitir excepciones comprobadas para Runnable#run
, había que declararlas en la interfaz de Runnable
y teníamos que try/catch
cada vez que Runnable
un thread.
Por otra parte, generalmente no llamamos al método de run
, simplemente lo implementamos. Comenzamos start()
un subproceso y luego, de alguna manera, se llama al método de run
.
Pero la razón más obvia: cuando iniciamos los hilos, normalmente no queremos esperar hasta que el método de run
finalice solo para detectar excepciones como esta:
try {
new Worker().start(); // now wait until run has finished
} catch (SomeThreadException oops) {
// handle checked exception
}
¿Alguien puede explicar el razonamiento detrás de esto?
Sí, porque cualquier excepción que arroje en el método de run
será ignorada cuidadosamente por JVM. Por lo tanto, arrojarlo allí probablemente sea un error (a menos que tengas un controlador de excepción específico para el hilo, mira los documentos sobre eso). No hay razón para incitar un comportamiento potencialmente erróneo.
O, con un ejemplo.
class MyThread extends Thread {
public void run() {
throw new RuntimeException();
}
}
...
new MyThread().start();
// here thread dies silently with no visible effects at all
editar
¿Por qué el hilo padre no puede "atrapar" la excepción del hilo "hijo" engendrado?
@ chaotic3quilibrium ya ha notado en su comentario por qué no: porque el hilo padre probablemente ya se haya movido.
new MyThread().start(); // launch thread and forget
// 1000 lines of code further...
i = i + 1; // would you like exception from child thread to be propagated here?