Java主动从当前线程获取异常信息
使用场景
在单个方法内主动捕获异常,并将异常的错误栈信息以日志的方式打出来
写法
当前方法throw 了 异常,即改方法存在异常的情况,则可以使用如下方式获取当前线程中的异常:
1 2 3 4 5 | // 主动获取当前线程异常栈信息 StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); // 以日志的方式打出 n 每一行错误栈信息结束后换行 log.error(StringUtils.join(stackTraceElements, 'n' )); |
Java捕获并处理线程异常:Thread及ThreadPoolExecutor线程池异常捕获
通过Thread.UncaughtExceptionHandler捕获线程异常
Thread.UncaughtExceptionHandler类的作用:捕获并处理线程run方法抛出的异常。
使用示例
为单个线程设置异常捕获
1 2 3 4 5 6 7 8 9 | Thread.UncaughtExceptionHandler exceptionHandler = (t, e) -> { System.out.println( "报错线程:" + t.getName()); System.out.println( "线程抛出的异常:" + e); }; Thread thread = new Thread(() -> { throw new RuntimeException( "throw a new Exception ! " ); }); thread.setUncaughtExceptionHandler(exceptionHandler); // 设置异常处理器 thread.start(); |
如果项目中,全局的Thread线程处理异常的方式都相同,那么可以设置一个全局的异常捕获类。
1 2 3 4 5 | Thread.UncaughtExceptionHandler exceptionHandler = (t, e) -> { System.out.println( "报错线程:" + t.getName()); System.out.println( "线程抛出的异常:" + e); }; Thread.setDefaultUncaughtExceptionHandler(exceptionHandler); |
通过这种方式,后续的Thread都可以公用这个异常处理类。如果需要用其它方式处理异常时,只需要实现1中的内容即可。
部分源码解析
1 2 3 4 5 6 | UncaughtExceptionHandler源码 // Thread.UncaughtExceptionHandler @FunctionalInterface public interface UncaughtExceptionHandler { void uncaughtException(Thread t, Throwable e); } |
Thread中的UncaughtExceptionHandler属性
1 2 3 4 | // 作用于当个Thread线程 private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // static修饰,其可以作用于所有Thread线程 private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; |
在Thread中,会首先提供Thread私有的异常处理类,然后才是全局
1 2 3 4 | public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } |
实现原理
当线程由于未捕获的异常而即将终止时,Java虚拟机将使用调用线程的getUncaughtExceptionHandler方法,以此来获取UncaughtExceptionHandler类,并执行其对异常的处理方法。
如果一个线程没有显式实现UncaughtExceptionHandler ,那么它的ThreadGroup对象将充当它的UncaughtExceptionHandler类。
1 2 3 4 5 | // Thread#getUncaughtExceptionHandler public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } |
ThreadGroup实现了UncaughtExceptionHandler类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class ThreadGroup implements Thread.UncaughtExceptionHandler { // …… public void uncaughtException(Thread t, Throwable e) { // 父级ThreadGroup的处理方法 if (parent != null ) { parent.uncaughtException(t, e); } else { // Thread 的全局默认处理方法 Thread.UncaughtExceptionHandler ueh = Thread.getDefaultUncaughtExceptionHandler(); if (ueh != null ) { ueh.uncaughtException(t, e); } else if (!(e instanceof ThreadDeath)) { System.err.print( "Exception in thread " " + t.getName() + "" "); e.printStackTrace(System.err); } } } // …… } |
ThreadPoolExecutor线程池异常捕获
使用示例
要捕获ThreadPoolExecutor线程池中的线程执行异常,需要实现被protected修饰的方法afterExecute,在该方法里面处理异常即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // ThreadPoolExecutor#afterExecute class ExtendedExecutor extends ThreadPoolExecutor { protected void afterExecute(Runnable r, Throwable t) { super .afterExecute(r, t); // 如果Runnable是Future类型,那么异常将会直接通过Future返回 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 } } // 非Future类型 if (t != null ) System.out.println(t); } } |
注意:实现afterExecute方法时,要正确嵌套多个覆盖,子类通常应在此方法的开头调用super.afterExecute,以确保不会破坏其他父类方法的实现。
源码解析
在ThreadPoolExecutor中,线程任务的实际执行方法是runWorker,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // ThreadPoolExecutor#runWorker final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; // 实际提交的任务 // ………… try { beforeExecute(wt, task); // task执行前的操作 Throwable thrown = null ; // 异常信息保存 try { task.run(); // 执行任务 } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); // task执行后的操作,也就包括了异常的捕获工作 } } finally { task = null ; w.completedTasks++; w.unlock(); } // …… } |
afterExecute方法在ThreadPoolExecutor,并没有进行任何操作,就是对异常的线程静默处理
1 2 | // ThreadPoolExecutor#afterExecute protected void afterExecute(Runnable r, Throwable t) { } |
Callable类型的任务,在执行时会自己捕获并维护执行中的异常。
1 2 3 4 5 6 7 8 9 10 11 12 | // AbstractExecutorService#submit public Future submit(Callable task) { if (task == null ) throw new NullPointerException(); RunnableFuture ftask = newTaskFor(task); execute(ftask); return ftask; } // Callable任务会被封装成FutureTask protected RunnableFuture newTaskFor(Callable callable) { return new FutureTask(callable); } |
在FutureTask的run方法中,他们内部会将整个任务用try-catch给包起来。因此,也不会抛出Callable#run执行的内部异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // FutureTask#run public void run() { // …… try { Callable c = callable; // …… // 这里将Callable的执行过程产生的异常都捕获了 try { result = c.call(); ran = true ; } catch (Throwable ex) { result = null ; ran = false ; setException(ex); } if (ran) set(result); } } finally { // …… |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。