Cómo atrapar una excepción de un hilo

Tengo Java clase principal, en la clase, comienzo un nuevo hilo, en el principal, espera hasta que el hilo muera. En algún momento, lanzo una excepción de tiempo de ejecución desde el hilo, pero no puedo ver la excepción lanzada desde el hilo en la clase principal.

Aquí está el código:

public class Test extends Thread { public static void main(String[] args) throws InterruptedException { Test t = new Test(); try { t.start(); t.join(); } catch(RuntimeException e) { System.out.println("** RuntimeException from main"); } System.out.println("Main stoped"); } @Override public void run() { try { while(true) { System.out.println("** Started"); sleep(2000); throw new RuntimeException("exception from thread"); } } catch (RuntimeException e) { System.out.println("** RuntimeException from thread"); throw e; } catch (InterruptedException e) { } } } 

¿Alguien sabe por qué?

Use un Thread.UncaughtExceptionHandler .

 Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); } }; Thread t = new Thread() { public void run() { System.out.println("Sleeping ..."); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Interrupted."); } System.out.println("Throwing exception ..."); throw new RuntimeException(); } }; t.setUncaughtExceptionHandler(h); t.start(); 

Esto explica la transición de estado de los hilos que dependen de si se produjeron o no excepciones:

Hilos y manejo de excepciones

Esto se debe a que las excepciones son locales a un hilo y su hilo principal no ve realmente el método de run . Le sugiero que lea más acerca de cómo funciona el proceso de subprocesos, pero para resumir rápidamente: su llamada para start inicia un hilo diferente, totalmente ajeno a su hilo principal. La llamada para join simplemente espera a que se haga. Una excepción que se lanza en un subproceso y que nunca se captura la finaliza, por lo que join devuelve en su subproceso principal, pero la excepción en sí se pierde.

Si quieres estar al tanto de estas excepciones no detectadas, puedes probar esto:

 Thread.setDefaultExceptionHandler(new UncaughtExceptionHandler() { public void unchaughtException(Thread t, Throwable e) { System.out.println("Caught " + e); } }); 

Puede encontrar más información sobre el manejo de excepciones no detectadas aquí .

Más probable;

  • no es necesario pasar la excepción de un hilo a otro.
  • si desea manejar una excepción, simplemente hágalo en el hilo que la arrojó.
  • su hilo principal no necesita esperar del hilo de fondo en este ejemplo, lo que en realidad significa que no necesita un hilo de fondo en absoluto.

Sin embargo, supongamos que necesita manejar una excepción de un hilo hijo otro. Yo usaría un ExecutorService así:

 ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Callable() { @Override public Void call() throws Exception { System.out.println("** Started"); Thread.sleep(2000); throw new IllegalStateException("exception from thread"); } }); try { future.get(); // raises ExecutionException for any uncaught exception in child } catch (ExecutionException e) { System.out.println("** RuntimeException from thread "); e.getCause().printStackTrace(System.out); } executor.shutdown(); System.out.println("** Main stopped"); 

huellas dactilares

 ** Started ** RuntimeException from thread java.lang.IllegalStateException: exception from thread at Main$1.call(Main.java:11) at Main$1.call(Main.java:6) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662) ** Main stopped 

Por favor, eche un vistazo a Thread.UncaughtExceptionHandler

Una forma mejor (alternativa) es usar Callable y Futuro para obtener el mismo resultado …

Use Callable lugar de Thread, luego puede llamar Future#get() que arroja cualquier excepción que arrojó el Callable.

Actualmente solo está capturando RuntimeException , una subclase de Exception . Pero su aplicación puede arrojar otras subclases de Excepción . Captura Exception genérica además de RuntimeException

Dado que muchas cosas se han cambiado en el frente de enhebrado, use la API java avanzada.

Prefiere la API java.util.concurrent avanzada para multi-threading como ExecutorService o ThreadPoolExecutor .

Puede personalizar su ThreadPoolExecutor para manejar excepciones.

Ejemplo de la página de documentación de Oracle:

Anular

 protected void afterExecute(Runnable r, Throwable t) 

Método invocado al finalizar la ejecución del Runnable dado. Este método es invocado por el hilo que ejecutó la tarea. Si no es nulo, el Throwable es el RuntimeException o Error no capturado que provocó que la ejecución finalizara abruptamente.

Código de ejemplo:

 class ExtendedExecutor extends ThreadPoolExecutor { // ... protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t == null && r instanceof Future) { try { Object result = ((Future) r).get(); } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // ignore/reset } } if (t != null) System.out.println(t); } } 

Uso:

 ExtendedExecutor service = new ExtendedExecutor(); 

He agregado un constructor sobre el código anterior como:

  public ExtendedExecutor() { super(1,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue(100)); } 

Puede cambiar este constructor para que se adapte a sus necesidades sobre el número de hilos.

 ExtendedExecutor service = new ExtendedExecutor(); service.submit(); 

¿Jugaste con setDefaultUncaughtExceptionHandler () y los métodos similares de la clase Thread? Desde la API: “Al configurar el controlador de excepciones no detectadas predeterminado, una aplicación puede cambiar la forma en que se manejan las excepciones no detectadas (como el inicio de sesión en un dispositivo o archivo específico) para aquellos hilos que ya aceptarían el comportamiento” predeterminado ” sistema proporcionado “.

Puede encontrar la respuesta a su problema allí … ¡buena suerte! 🙂

Enfrenté el mismo problema … poco trabajo (solo para objetos de implementación no anónimos) … podemos declarar el objeto de excepción de nivel de clase como nulo … luego inicializarlo dentro del bloque catch para el método de ejecución … si hay fue un error en el método de ejecución, esta variable no será nula … entonces podemos tener una comprobación nula para esta variable en particular y si no es nula, entonces hubo una excepción dentro de la ejecución de la secuencia.

 class TestClass implements Runnable{ private Exception ex; @Override public void run() { try{ //business code }catch(Exception e){ ex=e; } } public void checkForException() throws Exception { if (ex!= null) { throw ex; } } } 

llame a checkForException () después de join ()

Casi siempre es incorrecto extender Thread . No puedo afirmar esto con la suficiente fuerza.

Regla de Thread múltiple n. ° 1: extender el Thread es incorrecto. *

Si implementa Runnable en su lugar, verá su comportamiento esperado.

 public class Test implements Runnable { public static void main(String[] args) { Test t = new Test(); try { new Thread(t).start(); } catch (RuntimeException e) { System.out.println("** RuntimeException from main"); } System.out.println("Main stoped"); } @Override public void run() { try { while (true) { System.out.println("** Started"); Thread.sleep(2000); throw new RuntimeException("exception from thread"); } } catch (RuntimeException e) { System.out.println("** RuntimeException from thread"); throw e; } catch (InterruptedException e) { } } } 

produce;

 Main stoped ** Started ** RuntimeException from threadException in thread "Thread-0" java.lang.RuntimeException: exception from thread at Test.run(Test.java:23) at java.lang.Thread.run(Thread.java:619) 

* a menos que desee cambiar la forma en que su aplicación usa los hilos, lo que en el 99.9% de los casos no lo hace. Si crees que estás en el 0.1% de los casos, consulta la regla # 1.

Si implementa Thread.UncaughtExceptionHandler en la clase que inicia los Threads, puede establecer y luego volver a lanzar la excepción:

 public final class ThreadStarter implements Thread.UncaughtExceptionHandler{ private volatile Throwable initException; public void doSomeInit(){ Thread t = new Thread(){ @Override public void run() { throw new RuntimeException("UNCAUGHT"); } }; t.setUncaughtExceptionHandler(this); t.start(); t.join(); if (initException != null){ throw new RuntimeException(initException); } } @Override public void uncaughtException(Thread t, Throwable e) { initException = e; } } 

Lo que causa el siguiente resultado:

 Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: UNCAUGHT at com.gs.gss.ccsp.enrichments.ThreadStarter.doSomeInit(ThreadStarter.java:24) at com.gs.gss.ccsp.enrichments.ThreadStarter.main(ThreadStarter.java:38) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) Caused by: java.lang.RuntimeException: UNCAUGHT at com.gs.gss.ccsp.enrichments.ThreadStarter$1.run(ThreadStarter.java:15) 

Manejo de excepciones en subprocesos: por defecto, el método run () no arroja ninguna excepción, por lo que todas las excepciones comprobadas dentro del método de ejecución deben capturarse y manejarse solo allí y para las excepciones en tiempo de ejecución, podemos usar UncaughtExceptionHandler. UncaughtExceptionHandler es una interfaz proporcionada por Java para manejar excepciones en un método de ejecución de subprocesos. Entonces podemos implementar esta interfaz y volver a establecer nuestra clase de implementación en el objeto Thread utilizando el método setUncaughtExceptionHandler (). Pero este controlador debe configurarse antes de llamar a start () en la banda de rodadura.

si no configuramos uncaughtExceptionHandler, Threads ThreadGroup actúa como un manejador.

  public class FirstThread extends Thread { int count = 0; @Override public void run() { while (true) { System.out.println("FirstThread doing something urgent, count : " + (count++)); throw new RuntimeException(); } } public static void main(String[] args) { FirstThread t1 = new FirstThread(); t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { System.out.printf("Exception thrown by %s with id : %d", t.getName(), t.getId()); System.out.println("\n"+e.getClass()); } }); t1.start(); } } 

Buena explicación dada en http://coder2design.com/thread-creation/#exceptions

Mi solución con RxJava:

 @Test(expectedExceptions = TestException.class) public void testGetNonexistentEntry() throws Exception { // using this to work around the limitation where the errors in onError (in subscribe method) // cannot be thrown out to the main thread AtomicReference ex = new AtomicReference<>(); URI id = getRandomUri(); canonicalMedia.setId(id); client.get(id.toString()) .subscribe( m -> fail("Should not be successful"), e -> ex.set(new TestException())); for(int i = 0; i < 5; ++i) { if(ex.get() != null) throw ex.get(); else Thread.sleep(1000); } Assert.fail("Cannot find the exception to throw."); } 

No puedes hacer esto, ya que realmente no tiene sentido. Si no ha llamado a t.join() , su hilo principal podría estar en cualquier parte del código cuando el hilo t arroje una excepción.