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

代码如下:

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

    }
  }
}

有人知道为什么吗?


当前回答

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

其他回答

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

AtomicReference也是一个将错误传递给主线程的解决方案。与Dan Cruz的方法相同。

AtomicReference<Throwable> errorReference = new AtomicReference<>();

    Thread thread = new Thread() {
        public void run() {
            throw new RuntimeException("TEST EXCEPTION");

        }
    };
    thread.setUncaughtExceptionHandler((th, ex) -> {
        errorReference.set(ex);
    });
    thread.start();
    thread.join();
    Throwable newThreadError= errorReference.get();
    if (newThreadError!= null) {
        throw newThreadError;
    }  

唯一的变化是你可以使用AtomicReference而不是创建一个volatile变量,它在幕后做了同样的事情。

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

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

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

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

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

关于未捕获异常处理的更多信息可以在这里找到。

我也遇到过同样的问题……很少的工作(只用于实现而不是匿名对象)…我们可以将类级异常对象声明为null…然后在catch块for run方法中初始化它…如果在run方法中有错误,这个变量不会为空。然后,我们可以对这个特定的变量进行空检查,如果它不是空,那么线程执行内部就有异常。

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

join()后调用checkForException()