Comment attraper une exception à partir d’un thread

J’ai la classe principale de Java, dans la classe, je commence un nouveau thread, en général, il attend que le thread meure. À un moment donné, je lance une exception d’exécution à partir du thread, mais je ne peux pas intercepter l’exception générée par le thread dans la classe principale.

Voici le code:

public class Test extends Thread { public static void main(Ssortingng[] 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) { } } } 

Quelqu’un sait pourquoi?

Utilisez 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(); 

Cela explique la transition d’état des threads selon que des exceptions se sont produites ou non:

Gestion des threads et des exceptions

C’est parce que les exceptions sont locales à un thread et que votre thread principal ne voit pas réellement la méthode run . Je vous suggère de lire plus sur le fonctionnement du threading, mais de résumer rapidement: votre appel au start démarre un thread différent, totalement indépendant de votre thread principal. L’appel à se join attend simplement qu’il soit fait. Une exception qui est lancée dans un thread et jamais interceptée le termine, ce qui explique pourquoi la join retourne sur votre thread principal, mais l’exception elle-même est perdue.

Si vous voulez connaître ces exceptions non détectées, vous pouvez essayer ceci:

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

Vous trouverez plus d’informations sur la gestion des exceptions non capturées ici .

Probablement;

  • vous n’avez pas besoin de passer l’exception d’un thread à un autre.
  • Si vous voulez gérer une exception, faites-le simplement dans le thread qui l’a lancé.
  • Votre thread principal n’a pas besoin d’attendre du thread d’arrière-plan dans cet exemple, ce qui signifie que vous n’avez pas besoin de thread d’arrière-plan.

Cependant, supposons que vous ayez besoin de gérer une exception d’un autre thread enfant. J’utiliserais un ExecutorService comme celui-ci:

 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"); 

estampes

 ** 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 

S’il vous plaît jeter un oeil à Thread.UncaughtExceptionHandler

Le meilleur moyen (alternatif) est d’utiliser Callable et Future pour obtenir le même résultat …

Utilisez Callable place de Thread, vous pouvez alors appeler Future#get() qui lève toutes les exceptions lancées par Callable.

Actuellement, vous RuntimeException que RuntimeException , une sous-classe d’ Exception . Mais votre application peut lancer d’autres sous-classes d’ Exception . Catch Exception générique en plus de RuntimeException

Depuis que beaucoup de choses ont été changées sur Threading avant, utilisez une API Java avancée.

Préférer avance API java.util.concurrent pour le multi-threading comme ExecutorService ou ThreadPoolExecutor .

Vous pouvez personnaliser votre ThreadPoolExecutor pour gérer les exceptions.

Exemple de la page de documentation d’Oracle:

Passer outre

 protected void afterExecute(Runnable r, Throwable t) 

Méthode invoquée à la fin de l’exécution du Runnable donné. Cette méthode est appelée par le thread qui a exécuté la tâche. Si non nul, le Throwable est l’exception RuntimeException ou Erreur non interceptée qui a provoqué l’arrêt brutal de l’exécution.

Exemple de code:

 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); } } 

Usage:

 ExtendedExecutor service = new ExtendedExecutor(); 

J’ai ajouté un constructeur au-dessus du code ci-dessus en tant que:

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

Vous pouvez modifier ce constructeur en fonction de vos besoins en nombre de threads.

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

Avez-vous joué avec setDefaultUncaughtExceptionHandler () et les méthodes similaires de la classe Thread? A partir de l’API: “En définissant le gestionnaire d’exceptions non capturé par défaut, une application peut modifier le traitement des exceptions non capturées (telles que la connexion à un périphérique ou à un fichier spécifique) pour les threads acceptant déjà le comportement” par défaut “. système fourni. ”

Vous pouvez trouver la réponse à votre problème là-bas … bonne chance! 🙂

J’ai rencontré le même problème … peu de travail autour (uniquement pour l’implémentation des objects non anonymes) … nous pouvons déclarer l’object exception de niveau classe comme null … puis l’initialiser dans le bloc catch pour la méthode run … s’il y en a était une erreur dans la méthode run, cette variable ne sera pas nulle .. on peut alors avoir une vérification nulle pour cette variable particulière et si ce n’est pas nul alors il y avait une exception dans l’exécution du thread.

 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; } } } 

appelez checkForException () après la jointure ()

Il est presque toujours erroné d’étendre Thread . Je ne peux pas le dire assez fort.

Règle multithreading n ° 1: l’extension du Thread est incorrecte. *

Si vous implémentez Runnable place, vous verrez votre comportement attendu.

 public class Test implements Runnable { public static void main(Ssortingng[] 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) { } } } 

produit;

 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) 

* sauf si vous souhaitez modifier la façon dont votre application utilise les threads, ce qui, dans 99,9% des cas, ne le fait pas. Si vous pensez que vous êtes dans le 0,1% des cas, veuillez consulter la règle n ° 1.

Si vous implémentez Thread.UncaughtExceptionHandler dans la classe qui démarre les threads, vous pouvez définir et renvoyer l’exception:

 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; } } 

Qui provoque la sortie suivante:

 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) 

Gestion des exceptions dans Thread: Par défaut, la méthode run () ne génère aucune exception. Par conséquent, toutes les exceptions vérifiées dans la méthode run doivent être capturées et traitées uniquement et, pour les exceptions d’exécution, nous pouvons utiliser UncaughtExceptionHandler. UncaughtExceptionHandler est une interface fournie par Java pour gérer les exceptions dans une méthode Thread Run. Nous pouvons donc implémenter cette interface et remettre notre classe d’implémentation sur l’object Thread à l’aide de la méthode setUncaughtExceptionHandler (). Mais ce gestionnaire doit être défini avant d’appeler start () sur la piste.

Si nous ne définissons pas uncaughtExceptionHandler, Threads ThreadGroup agit comme un gestionnaire.

  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(Ssortingng[] 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(); } } 

Belle explication donnée à http://coder2design.com/thread-creation/#exceptions

Ma solution avec 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."); } 

Vous ne pouvez pas faire cela, car cela n’a pas vraiment de sens. Si vous n’aviez pas appelé t.join() alors votre thread principal pourrait être n’importe où dans le code lorsque le thread t lève une exception.