wake-up-neo.net

Wie fange ich eine Exception aus einem Thread ab?

Ich habe Java-Hauptklasse, in der Klasse beginne ich einen neuen Thread, in der Hauptsache wartet er, bis der Thread stirbt. Zu einem bestimmten Zeitpunkt löse ich eine Laufzeitausnahme aus dem Thread aus, aber ich kann die Ausnahme nicht erkennen, die aus dem Thread in der Hauptklasse ausgelöst wird. 

Hier ist der 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)
    {

    }
  }
}

Weiß jemand warum?

135
NARU

Verwenden Sie einen 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

Dies erklärt den Statusübergang von Threads abhängig davon, ob eine Ausnahme aufgetreten ist oder nicht:

Threads and Exception Handling

36

Das liegt daran, dass Ausnahmen lokal für einen Thread sind und Ihr Haupt-Thread die run-Methode nicht sieht. Ich schlage vor, dass Sie mehr darüber lesen, wie Threading funktioniert, aber um es kurz zusammenzufassen: Ihr Aufruf an start startet einen anderen Thread, der nicht mit Ihrem Haupt-Thread zusammenhängt. Der Aufruf von join wartet einfach darauf, dass es ausgeführt wird. Eine Ausnahme, die in einem Thread geworfen wird und nie abgefangen wird, beendet ihn. Daher wird join in Ihrem Haupt-Thread zurückgegeben. Die Ausnahme selbst geht jedoch verloren.

Wenn Sie sich dieser nicht erfassten Ausnahmen bewusst sein möchten, können Sie Folgendes versuchen:

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

Weitere Informationen zur nicht erfassten Ausnahmebehandlung finden Sie hier .

34
abyx

Höchstwahrscheinlich; 

  • sie müssen die Ausnahme nicht von einem Thread an einen anderen übergeben.
  • wenn Sie eine Ausnahme behandeln möchten, tun Sie dies einfach in dem Thread, der sie ausgelöst hat.
  • ihr Haupt-Thread muss in diesem Beispiel nicht vom Hintergrund-Thread warten, was bedeutet, dass Sie überhaupt keinen Hintergrund-Thread benötigen.

Nehmen wir jedoch an, Sie müssen eine Ausnahme von einem anderen untergeordneten Thread behandeln. Ich würde einen ExecutorService wie folgt verwenden:

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

druckt

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

Bitte werfen Sie einen Blick auf Thread.UncaughtExceptionHandler

Besserer (alternativer) Weg ist die Verwendung von Callable und Future , um dasselbe Ergebnis zu erzielen ...

5
denis.solonenko

Verwenden Sie Callable anstelle von Thread. Dann können Sie Future#get() aufrufen, wodurch eine Ausnahme ausgelöst wird, die Callable ausgelöst hat.

2
artbristol

Momentan erfassen Sie nur RuntimeException, eine Unterklasse von Exception. Ihre Anwendung kann jedoch andere Unterklassen von Exception werfen. Generisches Exception zusätzlich zu RuntimeException abfangen

Da in Threading viele Änderungen vorgenommen wurden, sollten Sie die erweiterte Java-API verwenden. 

Ziehen Sie Java.util.concurrent API für Multithreading wie ExecutorService oder ThreadPoolExecutor vor. 

Sie können Ihren ThreadPoolExecutor anpassen, um Ausnahmen zu behandeln.

Beispiel von der Oracle-Dokumentationsseite:

Überschreiben 

protected void afterExecute(Runnable r,
                            Throwable t)

Methode, die nach Abschluss der Ausführung des angegebenen Runable aufgerufen wird. Diese Methode wird von dem Thread aufgerufen, der die Task ausgeführt hat. Bei Nicht-NULL ist Throwable die nicht erfasste RuntimeException oder Error, die dazu geführt hat, dass die Ausführung abrupt abgebrochen wurde.

Beispielcode:

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

Verwendungszweck:

ExtendedExecutor service = new ExtendedExecutor();

Ich habe einen Konstruktor über dem obigen Code hinzugefügt als:

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

Sie können diesen Konstruktor an Ihre Anzahl der Threads anpassen. 

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

Ich war mit dem gleichen Problem konfrontiert ... wenig umgearbeitet (nur für die Implementierung nicht anonymer Objekte) ... wir können das Exception-Objekt der Klassenebene als null deklarieren ... und es dann innerhalb des catch-Blocks für die run-Methode initialisieren ... wenn vorhanden war ein Fehler in der run-Methode, diese Variable wird nicht null sein. Wir können dann eine Nullprüfung für diese bestimmte Variable durchführen. Wenn diese Variable nicht null ist, gab es eine Ausnahme innerhalb der Thread-Ausführung. 

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

checkForException () nach join () aufrufen

2
Java Beginner

Haben Sie mit setDefaultUncaughtExceptionHandler () und den ähnlichen Methoden der Thread-Klasse herumgespielt? Von der API aus: "Durch das Festlegen des standardmäßigen nicht erfassten Ausnahmebehandlers kann eine Anwendung die Art und Weise ändern, in der nicht erfasste Ausnahmen behandelt werden (z. B. Protokollierung auf einem bestimmten Gerät oder in einer bestimmten Datei) für die Threads, die bereits das Standardverhalten der Standardtools akzeptieren System bereitgestellt. "

Dort finden Sie möglicherweise die Antwort auf Ihr Problem ... viel Glück! :-)

1
Dr. Snuggles

Auch von Java 8 können Sie Dan Cruz Antwort schreiben als:

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

Es ist fast immer falsch, Thread zu erweitern. Ich kann das nicht stark genug sagen.

Multithreading-Regel Nr. 1: Das Erweitern von Thread ist falsch. *

Wenn Sie stattdessen Runnable implementieren, wird Ihr erwartetes Verhalten angezeigt.

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

    }
  }
}

produziert;

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)

* Es sei denn, Sie möchten die Art und Weise ändern, in der Ihre Anwendung Threads verwendet, was in 99,9% der Fälle nicht der Fall ist. Wenn Sie der Meinung sind, dass Sie sich in den 0,1% der Fälle befinden, lesen Sie bitte Regel 1.

0
Qwerky

Wenn Sie Thread.UncaughtExceptionHandler in einer Klasse implementieren, die die Threads startet, können Sie die Ausnahme festlegen und erneut auslösen:

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

}

Was verursacht die folgende Ausgabe:

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

Ausnahmebehandlung in Thread: Standardmäßig gibt die run () - Methode keine Ausnahme aus. Daher müssen alle geprüften Ausnahmen innerhalb der run-Methode nur abgefangen und behandelt werden. Für Laufzeitausnahmen können wir UncaughtExceptionHandler verwenden. UncaughtExceptionHandler ist eine von Java bereitgestellte Schnittstelle zur Behandlung von Ausnahmen in einer Thread-Ausführungsmethode. So können wir diese Schnittstelle implementieren und die implementierende Klasse mit der setUncaughtExceptionHandler () -Methode auf das Thread-Objekt zurücksetzen. Dieser Handler muss jedoch gesetzt sein, bevor wir start () auf der Lauffläche aufrufen.

wenn Sie uncaughtExceptionHandler nicht festlegen, fungiert die Threads ThreadGroup als Handler.

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

Schöne Erklärung unter http://coder2design.com/thread-creation/#exceptions

0
Jatinder Pal

Meine Lösung mit 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