Java 如何从线程中捕获异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6546193/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 07:39:00  来源:igfitidea点击:

How to catch an Exception from a thread

javamultithreading

提问by NARU

I have Java main class, in the class, I start a new thread, in the main, it waits until the thread dies. At some moment, I throw a runtime exception from the thread, but I can't catch the exception thrown from the thread in the main class.

我有 Java 主类,在类中,我启动了一个新线程,在主类中,它一直等到线程死亡。在某个时刻,我从线程抛出了一个运行时异常,但是我无法在主类中捕获从线程抛出的异常。

Here is the 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)
    {

    }
  }
}

Anybody knows why?

有谁知道为什么?

采纳答案by Dan Cruz

Use a Thread.UncaughtExceptionHandler.

使用一个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();

回答by Mathias Schwarz

You cannot do this, since it doesn't really make sense. If you hadn't called t.join()then you main thread could be anywhere in the code when the tthread throws an exception.

你不能这样做,因为它真的没有意义。如果您没有调用,t.join()那么当t线程抛出异常时,您的主线程可能位于代码中的任何位置。

回答by abyx

That's because exceptions are local to a thread, and your main thread doesn't actually see the runmethod. I suggest you read more about how threading works, but to quickly summarize: your call to startstarts up a different thread, totally unrelated to your main thread. The call to joinsimply waits for it to be done. An exception that is thrown in a thread and never caught terminates it, which is why joinreturns on your main thread, but the exception itself is lost.

那是因为异常是线程本地的,而您的主线程实际上并没有看到该run方法。我建议您阅读有关线程如何工作的更多信息,但要快速总结一下:您对start启动不同线程的调用与您的主线程完全无关。调用join只是等待它完成。在线程中抛出但从未捕获的异常会终止它,这就是为什么join在主线程上返回,但异常本身丢失的原因。

If you want to be aware of these uncaught exceptions you can try this:

如果你想知道这些未捕获的异常,你可以试试这个:

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("Caught " + e);
    }
});

More information about uncaught exception handling can be found here.

可以在此处找到有关未捕获异常处理的更多信息。

回答by artbristol

Use Callableinstead of Thread, then you can call Future#get()which throws any exception that the Callable threw.

使用Callable而不是 Thread,然后您可以调用Future#get()which 抛出 Callable 抛出的任何异常。

回答by denis.solonenko

Please take a look at Thread.UncaughtExceptionHandler

请看一下Thread.UncaughtExceptionHandler

Better (alternative) way is to use Callableand Futureto get the same result...

更好的(替代)方法是使用CallableFuture来获得相同的结果......

回答by Dr. Snuggles

Did you play around with setDefaultUncaughtExceptionHandler() and the alike methods of the Thread class? From the API: "By setting the default uncaught exception handler, an application can change the way in which uncaught exceptions are handled (such as logging to a specific device, or file) for those threads that would already accept whatever "default" behavior the system provided."

你玩过 setDefaultUncaughtExceptionHandler() 和 Thread 类的类似方法吗?来自 API:“通过设置默认的未捕获异常处理程序,应用程序可以更改那些已经接受任何“默认”行为的线程处理未捕获异常的方式(例如记录到特定设备或文件)系统提供。”

You might find the answer to your problem there... good luck! :-)

您可能会在那里找到问题的答案……祝您好运!:-)

回答by Talha Ahmed Khan

This explains the state transition of threads dependening on whether an exceptions occured or not:

这解释了线程的状态转换取决于是否发生异常:

Threads and Exception Handling

线程和异常处理

Source : http://www-public.imtbs-tsp.eu/~gibson/Teaching/CSC7322/L8-ExceptionsAndThreads.pdf

来源:http: //www-public.imtbs-tsp.eu/~gibson/Teaching/CSC7322/L8-ExceptionsAndThreads.pdf

回答by Qwerky

It is almost always wrong to extend Thread. I cannot state this strongly enough.

扩展几乎总是错误的Thread。我不能足够强烈地说明这一点。

Multithreading Rule #1: Extending Threadis wrong.*

多线程规则#1:扩展Thread是错误的。*

If you implement Runnableinstead you will see your expected behaviour.

如果您Runnable改为实施,您将看到您预期的行为。

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

    }
  }
}

produces;

产生;

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)

* unless you want to change the way your application uses threads, which in 99.9% of cases you don't. If you think you are in the 0.1% of cases, please see rule #1.

* 除非您想更改应用程序使用线程的方式,在 99.9% 的情况下您不会这样做。如果您认为自己属于 0.1% 的情况,请参阅规则 #1。

回答by Peter Lawrey

Most likely;

最有可能的;

  • you don't need to pass the exception from one thread to another.
  • if you want to handle an exception, just do it in the thread which threw it.
  • your main thread doesn't need to wait from the background thread in this example, which actually means you don't need a background thread at all.
  • 您不需要将异常从一个线程传递到另一个线程。
  • 如果你想处理一个异常,只需在抛出它的线程中进行。
  • 在这个例子中,你的主线程不需要从后台线程等待,这实际上意味着你根本不需要后台线程。

However, lets assume you do need to handle an exception from a child thread another. I would use an ExecutorService like this:

但是,假设您确实需要处理来自另一个子线程的异常。我会使用这样的 ExecutorService:

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

prints

印刷

** Started
** RuntimeException from thread 
java.lang.IllegalStateException: exception from thread
    at Main.call(Main.java:11)
    at Main.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

回答by Stefano

If you implement Thread.UncaughtExceptionHandler in class that starts the Threads, you can set and then rethrow the exception:

如果在启动线程的类中实现 Thread.UncaughtExceptionHandler,则可以设置然后重新抛出异常:

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

}

Which causes the following output:

这会导致以下输出:

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.run(ThreadStarter.java:15)