这篇文章主要介绍了Java如何主动从当前线程获取异常信息,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
Java主动从当前线程获取异常信息
使用场景
在单个方法内主动捕获异常,并将异常的错误栈信息以日志的方式打出来
写法
当前方法throw 了 异常,即改方法存在异常的情况,则可以使用如下方式获取当前线程中的异常:
// 主动获取当前线程异常栈信息
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
// 以日志的方式打出 \n 每一行错误栈信息结束后换行
log.error(StringUtils.join(stackTraceElements, '\n'));
Java捕获并处理线程异常:Thread及ThreadPoolExecutor线程池异常捕获
通过Thread.UncaughtExceptionHandler捕获线程异常
Thread.UncaughtExceptionHandler类的作用:捕获并处理线程run方法抛出的异常。
使用示例
为单个线程设置异常捕获
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 {
// ……
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程学习网。
本文标题为:Java如何主动从当前线程获取异常信息
基础教程推荐
- Java文件管理操作的知识点整理 2023-05-19
- java实现多人聊天系统 2023-05-19
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- springboot自定义starter方法及注解实例 2023-03-31
- Java实现线程插队的示例代码 2022-09-03
- Java实现查找文件和替换文件内容 2023-04-06
- Java并发编程进阶之线程控制篇 2023-03-07
- Java数据结构之对象比较详解 2023-03-07
- java基础知识之FileInputStream流的使用 2023-08-11
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02