提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
前些天,在网上逛论坛的时候看到 UncaughtExceptionHandler ,貌似从来没用过,所以学习下
一、UncaughtExceptionHandler 是什么?
/**
* 当线程由于未捕获异常而突然终止时调用的处理程序接口。
* 当线程由于未捕获异常即将终止时,Java虚拟机将使用getUncaughtExceptionHandler查询线程的
* UncaughtExceptionHandler,并调用处理程序的uncaughtException方法,将线程和异常作为参数传递。
* 如果线程未显式设置其UncaughtExceptionHandler,则其ThreadGroup对象将充当其
* UncaughtExceptionHandler。
* 如果ThreadGroup对象对处理异常没有特殊要求,它可以将调用转发给默认的未捕获异常处理程序。
*/
public interface UncaughtExceptionHandler {
/**
* 方法,该方法在给定线程由于给定的未捕获异常而终止时调用。
* Java虚拟机将忽略此方法引发的任何异常。
* 参数:
* t–线程
* e–异常
*/
void uncaughtException(Thread t, Throwable e);
}
这个描述看取来 就是给程序员一个钩子函数,让程序可以感知到线程非正常关闭时的情况,可以打日志 或者 啥操作
二、使用步骤
1.通过try-catch捕捉子线程的问题
public class Test {
public static void main(String[] args) {
try {
Thread thread = new Thread(new Task());
thread.start();
} catch (Exception e) {
System.out.println("==Exception: " + e.getMessage());
}
}
}
class Task implements Runnable {
@Override
public void run() {
System.out.println(1 / 0);
}
}
输出
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at com.example.demo.config.Task.run(ttt.java:33)
at java.lang.Thread.run(Thread.java:745)
可以看到,其实catch 并未捕捉到 异常,而子线程的错误 抛出到虚拟机了
2.非线程池设置
通过设置线程的UncaughtExceptionHandler 则可以捕捉到异常
public class Test {
public static void main(String args[]) {
Thread thread = new Thread(new Task());
thread.setUncaughtExceptionHandler(new ExceptionHandlerDemo());
thread.start();
}
}
class ExceptionHandlerDemo implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("whp Exception: " + e.getMessage());
}
}
class Task implements Runnable {
@Override
public void run() {
System.out.println(1 / 0);
}
}
输出:
whp Exception: / by zero
3.线程池的情况下设置UncaughtExceptionHandler
public class testClass {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolTaskExecutor executorService = buildThreadPoolTaskExecutor();
//这里注意先是execute ,如果是submit 则有些不同 需要get 才能出现错误
executorService.execute(() -> run("execute方法"));
}
private static void run(String name) {
String printStr = "【thread-name:" + Thread.currentThread().getName() + ",执行方式:" + name+"】";
System.out.println(printStr);
throw new RuntimeException(printStr + ",出现异常");
}
private static ThreadPoolTaskExecutor buildThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor executorService = new ThreadPoolTaskExecutor();
executorService.setCorePoolSize(5);
executorService.setMaxPoolSize(5);
executorService.setQueueCapacity(10);
executorService.setKeepAliveSeconds(10);
executorService.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executorService.setThreadFactory(new DefineThreadFactory("小王"));
executorService.initialize();
return executorService;
}
}
import io.netty.util.concurrent.DefaultThreadFactory;
/**
* 这里图简单 ,继承了一个ThreadFactory实现
*/
public class DefineThreadFactory extends DefaultThreadFactory {
public DefineThreadFactory(String poolName) {
super(poolName);
}
@Override
public Thread newThread(Runnable r) {
Thread t = super.newThread(r);
//给生成的线程设置一个错误处理器
t.setUncaughtExceptionHandler(new ExceptionHandler());
return t;
}
}
public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("define==Exception: " + e.getMessage());
}
}
输出:
09:55:09.844 [main] INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Initializing ExecutorService
【thread-name:小王-1-1,执行方式:execute方法】
define==Exception: 【thread-name:小王-1-1,执行方式:execute方法】,出现异常
可以看到 线程池中工作线程 的错误也被捕捉到了
总结
UncaughtExceptionHandler API 提供了一个优雅的方式,让我们可以处理线程的异常错误。