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?
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();
Ceci explique la transition d'état des threads selon qu'une exception est survenue ou non:
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 .
Probablement;
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
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 ...
Utilisez Callable
au lieu de Thread, vous pouvez appeler Future#get()
qui lève toutes les exceptions lancées par Callable.
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>);
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 ()
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! :-)
É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();
Il est presque toujours faux d'étendre Thread
. Je ne peux pas le dire assez fort.
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.
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)
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
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.");
}