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?
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();
Dies erklärt den Statusübergang von Threads abhängig davon, ob eine Ausnahme aufgetreten ist oder nicht:
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 .
Höchstwahrscheinlich;
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
Bitte werfen Sie einen Blick auf Thread.UncaughtExceptionHandler
Besserer (alternativer) Weg ist die Verwendung von Callable und Future , um dasselbe Ergebnis zu erzielen ...
Verwenden Sie Callable
anstelle von Thread. Dann können Sie Future#get()
aufrufen, wodurch eine Ausnahme ausgelöst wird, die Callable ausgelöst hat.
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>);
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
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! :-)
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();
Es ist fast immer falsch, Thread
zu erweitern. Ich kann das nicht stark genug sagen.
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.
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)
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
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.");
}