【异常】捕获线程池执行任务时产生的异常
前言:
在编写程序时,我们为了充分利用多核CPU、加快接口响应速度,通常会使用线程池来处理请求,但线程池执行任务过程中难免会出现异常,导致请求失败。那如果我们想在任务发生异常后捕获异常,并做一些”善后“操作,该如何做呢?
捕获方案:
1、try、catch:
public class ExceptionHandle {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
2,
30,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);
executor.execute(() -> {
try {
//模拟业务代码出现异常
int i = 1 / 0;
}catch (Exception e){
System.err.println(e.getMessage());
}
});
executor.shutdown();
}
}
原理:在编码时提前预知业务代码可能会发生异常,并用try块捕获业务异常,并在catch块做善后工作。
2、FutureTask:
public class ExceptionHandle {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
2,
30,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
//模拟业务代码出现异常
return 1/0;
}
};
FutureTask<Integer> task = new FutureTask<Integer>(callable);
executor.submit(task);
try {
task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
//这里捕获的是真正的业务异常
System.err.println(e.getCause().getMessage());
}
executor.shutdown();
}
}
原理:FutureTask技能作为任务提交给线程执行,也能像Future一样通过get()方法获取任务执行结果。如果任务正常执行,那么返回的就是结果对象,如果执行出现异常,返回的就是异常对象;当发生异常时,try块就能捕获异常,并在catch块做相应处理。get()方法实现如下
//可能会抛出InterruptedException、ExecutionException
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
InterruptedException是线程在等待执行结果时,被其它线程打断所产生的异常。
用Future接口获取submit()方法的返回结果也能达到同样的效果,对FutureTask不太了解的话,可以看我的这篇博客:【Java】浅析FutureTask的核心方法get-CSDN博客
3、ThreadFactor:
public class ExceptionHandle {
public static void main(String[] args) {
//线程工厂用于给线程池创建的线程加上一些特征
//你可以通过线程工厂指定线程的优先级、是否是守护线程等等
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
//
t.setUncaughtExceptionHandler((Thread thread,Throwable e) -> {
if(e != null){
System.err.println(e.getMessage());
}
});
return t;
}
};
ThreadPoolExecutor executor = new ThreadPoolExecutor(
1,
2,
30,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
factory
);
executor.execute(() -> {
//模拟业务异常
int i = 1/0;
});
executor.shutdown();
}
}
原理:
(1)ThreadFactor接口用于声明线程的属性,如线程的优先级、名称、守护进程状态等,它是线程池的构造参数之一;如果创建线程池时传入了ThreadFactor,线程池在创建线程时会按照ThreadFactor指定的形式来创建。
(2)我们让线程调用了setUncaughtExceptionHandler()方法,这个方法需要一个参数:UncaughtExceptionHandler接口,当线程由于未捕获的异常而突然终止时,会调用这个接口的uncaughtException()方法,我们可以自定义这个方法的实现逻辑,对异常做处理。相当于线程执行任务过程中如果出现异常,那么就会回调uncaughtException()方法,如下图所示。
如果本文对你有所帮助,不妨点个赞支持一下!