我有一个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)
    {

    }
  }
}

有人知道为什么吗?


当前回答

最有可能的;

您不需要将异常从一个线程传递到另一个线程。 如果你想处理异常,只需在抛出异常的线程中处理即可。 在本例中,主线程不需要从后台线程等待,这实际上意味着您根本不需要后台线程。

但是,让我们假设您确实需要处理来自子线程的异常。我将像这样使用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");

打印

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

其他回答

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

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

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

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

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

如果你实现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;
    }    

}

这将导致以下输出:

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)

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

请看看Thread。UncaughtExceptionHandler

更好的(替代)方法是使用Callable和Future来获得相同的结果…