如何从线程中捕获异常

NAR*_*ARU 153 java multithreading

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

这是代码:

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

    }
  }
}
Run Code Online (Sandbox Code Playgroud)

谁知道为什么?

Dan*_*ruz 206

用一个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();
Run Code Online (Sandbox Code Playgroud)

  • 如果我想将异常抛到上一级,我该怎么办? (13认同)
  • @rodi将ex保存到上层可以在处理程序中看到的volatile变量(例如成员变量).在外面,检查是否为null,否则抛出.或者使用新的volatile字段扩展UEH并在那里存储异常. (6认同)

aby*_*byx 37

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

如果您想了解这些未捕获的异常,可以尝试这样做:

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

有关未捕获的异常处理的更多信息,请参见此处.


Tal*_*han 37

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

线程和异常处理

  • 你创建了图表吗?如果没有,来源是什么? (9认同)

Pet*_*rey 20

最有可能的;

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

但是,假设您确实需要从另一个子线程处理异常.我会像这样使用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");
Run Code Online (Sandbox Code Playgroud)

版画

** 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
Run Code Online (Sandbox Code Playgroud)

  • 但是 `future.get()` 不会等待或阻塞直到线程完成执行吗? (2认同)

art*_*tol 8

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

  • 请注意,“Callable.call”中引发的异常被包装在“ExcecutionException”中,并且必须评估其原因。 (2认同)

den*_*nko 7

请看一下Thread.UncaughtExceptionHandler

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


Uta*_*dru 5

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;
    }  
Run Code Online (Sandbox Code Playgroud)

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