web-dev-qa-db-fra.com

Comment attraper une exception d'un fil

J'ai la classe principale Java, dans la classe, je commence un nouveau thread, dans l’ensemble, elle attend que le thread meure. À un moment donné, je lève une exception d'exécution à partir du thread, mais je ne peux pas attraper l'exception levée à partir du thread dans la classe principale. 

Voici le code:

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)
    {

    }
  }
}

Quelqu'un sait pourquoi?

135
NARU

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();
183
Dan Cruz

Ceci explique la transition d'état des threads selon qu'une exception est survenue ou non:

Threads and Exception Handling

36
Talha Ahmed Khan

En effet, les exceptions sont locales à un thread et votre thread principal ne voit pas la méthode run. Je vous suggère de lire davantage sur le fonctionnement des threads, mais pour résumer rapidement: votre appel à start démarre un autre thread, totalement indépendant de votre thread principal. L'appel à join attend simplement que cela soit fait. Une exception qui est levée dans un fil et qui n'est jamais interceptée le termine, raison pour laquelle join retourne sur votre thread principal, mais l'exception elle-même est perdue.

Si vous voulez être au courant de ces exceptions non capturées, vous pouvez essayer ceci:

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

Plus d'informations sur la gestion des exceptions non capturées peuvent être trouvées ici .

34
abyx

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 fil qui l'a lancée.
  • votre thread principal n'a pas besoin d'attendre du thread d'arrière-plan dans cet exemple, ce qui signifie en fait que vous n'avez pas besoin d'un thread d'arrière-plan.

Cependant, supposons que vous ayez besoin de gérer une exception d'un thread enfant par un autre. Je voudrais utiliser un ExecutorService comme ceci:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Void> future = executor.submit(new Callable<Void>() {
    @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");

empreintes

** 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
17
Peter Lawrey

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

Une meilleure façon (alternative) est d'utiliser Callable et Future pour obtenir le même résultat ...

5
denis.solonenko

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

2
artbristol

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

Comme beaucoup de choses ont été modifiées sur le front de threading, utilisez les API Java avancées. 

Préférez avance Java.util.concurrent API pour les multi-threads comme ExecutorService ou ThreadPoolExecutor

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

Exemple de la page de documentation 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-null, Throwable est l'exception RuntimeException ou Error non capturé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<Runnable>(100));
   }

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

ExtendedExecutor service = new ExtendedExecutor();
service.submit(<your Callable or Runnable implementation>);
2
Ravindra babu

J'ai rencontré le même problème ... peu de travail (uniquement pour les objets d'implémentation non anonymes) ... nous pouvons déclarer l'objet d'exception de niveau classe comme nul ... était une erreur dans la méthode d'exécution, cette variable ne sera pas nulle .. nous pouvons alors avoir la vérification nulle pour cette variable particulière et si ce n'est pas nul, 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 join ()

2
Java Beginner

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’exception par défaut non capturé, une application peut modifier la manière dont les exceptions non capturées sont traitées (comme la système fourni. "

Vous y trouverez peut-être la réponse à votre problème ... bonne chance! :-)

1
Dr. Snuggles

Également à partir de Java 8, vous pouvez écrire la réponse de Dan Cruz comme suit:

Thread t = new Thread(()->{
            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((th, ex)-> log(String.format("Exception in thread %d id: %s", th.getId(), ex)));
t.start();
1
Andr1i

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

Règle multithreading n ° 1: L'extension de la variable Thread est incorrecte. *

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

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) {

    }
  }
}

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 manière dont votre application utilise les threads, ce qui n'est pas le cas dans 99,9% des cas. Si vous pensez être dans 0,1% des cas, veuillez vous reporter à la règle 1.

0
Qwerky

Si vous implémentez Thread.UncaughtExceptionHandler dans la classe qui démarre les threads, vous pouvez définir puis retransmettre 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;
    }    

}

Ce 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)
0
Stefano

Gestion des exceptions dans les threads: Par défaut, la méthode run () ne lève aucune exception. Par conséquent, toutes les exceptions vérifiées dans la méthode run doivent être capturées et gérées uniquement ici. 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 d'exécution Thread. Nous pouvons donc implémenter cette interface et ramener notre classe d'implémentation à l'objet Thread à l'aide de la méthode setUncaughtExceptionHandler (). Mais ce gestionnaire doit être défini avant d'appeler start () sur la bande de roulement.

si nous ne définissons pas uncaughtExceptionHandler, le Thread ThreadGroup agit en tant que 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(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();
}
}

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

0
Jatinder Pal

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<Exception> 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.");
}
0
Zinan Xing