如何从线程捕获exception

我有Java的主类,在类中,我开始一个新的线程,在主要的,它等待,直到线程死亡。 在某些时候,我从线程抛出一个运行时exception,但是我无法捕捉到主类中的线程抛出的exception。

这里是代码:

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

有人知道为什么吗?

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

这解释了线程的状态转换依赖于是否发生exception:

线程和异常处理

这是因为exception是线程本地的,而你的主线程实际上并没有看到run方法。 我build议你多了解一下线程的工作原理,但是要快速总结一下:你的调用start启动一个与你的主线程完全无关的不同的线程。 join电话只是等待它的完成。 在线程中抛出exception并且永远不会终止它,这就是为什么在主线程上join返回,但是exception本身已经丢失。

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

 Thread.setDefaultExceptionHandler(new UncaughtExceptionHandler() { public void unchaughtException(Thread t, Throwable e) { System.out.println("Caught " + e); } }); 

有关未捕获的exception处理的更多信息可以在这里find。

最有可能的;

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

但是,让我们假设你需要处理另一个子线程的exception。 我会使用这样的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 

请看看Thread.UncaughtExceptionHandler

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

目前,您只捕获RuntimeExceptionException的子类。 但是你的应用程序可能会抛出exception的其他子类。 除RuntimeException Exception ,还捕获genericsException

由于很多东西已经在线程前端进行了更改,因此请使用高级的java API。

ExecutorServiceThreadPoolExecutor等multithreadingExecutorService java.util.concurrent API 。

您可以自定义您的ThreadPoolExecutor来处理exception。

来自oracle文档页面的示例:

覆盖

 protected void afterExecute(Runnable r, Throwable t) 

在完成给定Runnable的执行后调用的方法。 该方法由执行任务的线程调用。 如果非null,则Throwable是未捕获的RuntimeException或导致执行突然终止的错误。

示例代码:

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

用法:

 ExtendedExecutor service = new ExtendedExecutor(); 

我在上面的代码中添加了一个构造函数:

  public ExtendedExecutor() { super(1,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100)); } 

您可以更改此构造函数以适合您的线程数量要求。

 ExtendedExecutor service = new ExtendedExecutor(); service.submit(<your Callable or Runnable implementation>); 

使用Callable而不是Thread,那么你可以调用Future#get() ,它抛出了Callable抛出的exception。

你玩过setDefaultUncaughtExceptionHandler()和Thread类的类似方法吗? 从API:“通过设置默认的未捕获的exception处理程序,应用程序可以改变处理未捕获的exception的方式(比如logging到特定的设备或文件),这些线程已经可以接受任何”默认“系统提供“。

你可能会在那里find你的问题的答案…祝你好运! 🙂

扩展Thread几乎总是错误的。 我不能够强调这一点。

multithreading规则#1:扩展Thread是错误的*

如果你实现了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) { } } } 

产生;

 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) 

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

我遇到了同样的问题…只是为了实现而不是匿名对象…我们可以将类级别的exception对象声明为null …然后在catch块中为run方法初始化它…如果存在在运行方法是错误的,这个variables不会是空的..我们可以然后有空检查这个特定的variables,如果它不是空的,那么在线程执行内部有exception。

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

你可以设置主线程为你的第二个线程的uncaughtExceptionHandler。

线程API

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

 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) 

线程中的exception处理:默认情况下,run()方法不会抛出任何exception,因此run方法中所有检查的exception都必须在那里被捕获和处理,而对于运行时exception,我们可以使用UncaughtExceptionHandler。 UncaughtExceptionHandler是Java提供的一个接口,用于处理线程运行方法中的exception。 所以我们可以实现这个接口,并使用setUncaughtExceptionHandler()方法将我们的实现类返回给Thread对象。 但是在我们调用start()之前,必须设置这个处理函数。

如果我们不设置uncaughtExceptionHandler,那么线程ThreadGroup充当一个处理程序。

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

http://coder2design.com/thread-creation/#exceptions上给出了很好的解释;

我用RxJava解决scheme:

 @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."); } 

你不能这样做,因为它没有任何意义。 如果你没有调用t.join()那么你的主线程可能在代码中的任何地方,当t线程抛出一个exception。