目录
Java主动从当前线程获取异常信息
Java捕获并处理线程异常:Thread及ThreadPoolExecutor线程池异常捕获
通过Thread.UncaughtExceptionHandler捕获线程异常ThreadPoolExecutor线程池异常捕获
Java主动从当前线程获取异常信息
使用场景
在单个方法内主动捕获异常,并将异常的错误栈信息以日志的方式打出来
写法
当前方法throw 了 异常,即改方法存在异常的情况,则可以使用如下方式获取当前线程中的异常: - // 主动获取当前线程异常栈信息
- StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
- // 以日志的方式打出 \n 每一行错误栈信息结束后换行
- log.error(StringUtils.join(stackTraceElements, '\n'));
复制代码 Java捕获并处理线程异常:Thread及ThreadPoolExecutor线程池异常捕获
通过Thread.UncaughtExceptionHandler捕获线程异常
Thread.UncaughtExceptionHandler类的作用:捕获并处理线程run方法抛出的异常。
strong>使用示例</strong>
为单个线程设置异常捕获 - 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线程处理异常的方式都相同,那么可以设置一个全局的异常捕获类。 - Thread.UncaughtExceptionHandler exceptionHandler = (t, e) -> {
- System.out.println("报错线程:" + t.getName());
- System.out.println("线程抛出的异常:" + e);
- };
- Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
复制代码通过这种方式,后续的Thread都可以公用这个异常处理类。如果需要用其它方式处理异常时,只需要实现1中的内容即可。
部分源码解析- UncaughtExceptionHandler源码
- // Thread.UncaughtExceptionHandler
- @FunctionalInterface
- public interface UncaughtExceptionHandler {
- void uncaughtException(Thread t, Throwable e);
- }
复制代码Thread中的UncaughtExceptionHandler属性 - // 作用于当个Thread线程
- private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
- // static修饰,其可以作用于所有Thread线程
- private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
复制代码在Thread中,会首先提供Thread私有的异常处理类,然后才是全局 - public UncaughtExceptionHandler getUncaughtExceptionHandler() {
- return uncaughtExceptionHandler != null ?
- uncaughtExceptionHandler : group;
- }
复制代码 实现原理
当线程由于未捕获的异常而即将终止时,Java虚拟机将使用调用线程的getUncaughtExceptionHandler方法,以此来获取UncaughtExceptionHandler类,并执行其对异常的处理方法。
如果一个线程没有显式实现UncaughtExceptionHandler ,那么它的ThreadGroup对象将充当它的UncaughtExceptionHandler类。 - // Thread#getUncaughtExceptionHandler
- public UncaughtExceptionHandler getUncaughtExceptionHandler() {
- return uncaughtExceptionHandler != null ?
- uncaughtExceptionHandler : group;
- }
复制代码ThreadGroup实现了UncaughtExceptionHandler类。 - 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,在该方法里面处理异常即可。 - // 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,如下所示: - // 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,并没有进行任何操作,就是对异常的线程静默处理 - // ThreadPoolExecutor#afterExecute
- protected void afterExecute(Runnable r, Throwable t) { }
复制代码Callable类型的任务,在执行时会自己捕获并维护执行中的异常。 - // AbstractExecutorService#submit
- public <T> Future<T> submit(Callable<T> task) {
- if (task == null) throw new NullPointerException();
- RunnableFuture<T> ftask = newTaskFor(task);
- execute(ftask);
- return ftask;
- }
- // Callable任务会被封装成FutureTask
- protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
- return new FutureTask<T>(callable);
- }
复制代码在FutureTask的run方法中,他们内部会将整个任务用try-catch给包起来。因此,也不会抛出Callable#run执行的内部异常。 - // FutureTask#run
- public void run() {
- // ……
- try {
- Callable<V> c = callable;
- // ……
- // 这里将Callable的执行过程产生的异常都捕获了
- try {
- result = c.call();
- ran = true;
- } catch (Throwable ex) {
- result = null;
- ran = false;
- setException(ex);
- }
- if (ran)
- set(result);
- }
- } finally {
- // ……
复制代码以上为个人经验,希望能给大家一个参考,也希望大家多多支持中国红客联盟。 |