如何从线程捕获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来获得相同的结果…
目前,您只捕获RuntimeException
, Exception
的子类。 但是你的应用程序可能会抛出exception的其他子类。 除RuntimeException
Exception
,还捕获genericsException
由于很多东西已经在线程前端进行了更改,因此请使用高级的java API。
为ExecutorService
或ThreadPoolExecutor
等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。