前言:
先思考一个问题,为什么非要异常一定抛出给外层让外层进行捕获?因为外层有统一的异常处理逻辑,比如本文示例中业务流程中的审批失败发送告警通知。我们有时候希望对整个程序的异常处理都是用同一套异常处理方案,那么内部异常就一定要抛出给外层。
背景:
在工作中遇到一个关于多线程异常的问题,业务流程是在审批处理接口中有个多线程上线产品的操作,在程序运行过程中有用户反馈审批成功,但是产品没有上线,结果看日志排查发现其实在调用上线产品操作时是失败的,但是整个审批处理是成功的,没有异常,也没有发送告警通知。审批处理的伪代码如下:
// 审批处理
@Transactional(rollbackFor = Exception.class)
public void handleApproval() {
try {
if (agree) {
// 更新审批状态
// 上线产品:onLineProduct();
} else {
// 更新审批状态
}
} catch (Exception e) {
// 审批出现错误:打印log
// 发送告警通知:sendAlarm()
} finally {
// 耗时统计及打印
}
}
在上线产品onLineProduct()方法中,原伪代码如下:
private void onLineProduct() {
CountDownLatch countDownLatch = new CountDownLatch(sumCount);
for (sumCount) {
Runnable onlineTask = () -> {
try {
//上线
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
countDownLatch.countDown();
}
};
threadPool.submit(onlineTask);
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new Exception("err");
}
}
那么到底如何捕获多线程异常呢?123走起:
1、尝试throw 出异常,对onLineProduct()伪代码进行修改,核心逻辑如下:
for (sumCount) {
Runnable onlineTask = () -> {
try {
//上线
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new Exception("上线err");
} finally {
countDownLatch.countDown();
}
};
threadPool.submit(onlineTask);
}
结论:没有什么用,没有生效,上线失败后外层依然捕获不到,同样没有告警通知,百度搜索说多线程里面的异常外层无法捕获,因为是独立的。
2、简单粗暴,直接取消多线程,改成正常按顺序执行,有异常一定会被捕获到,也会发送告警通知。没毛病,但是万一确实需要多线程调用呢?
3、使用Callable处理多线程,Callable可以将异常抛出到调用线程,亲测有效,伪代码如下:
for (sumCount) {
//Callable可以将异常抛出到调用线程,参考文章:https://www.codenong.com/12305667/
Callable task = () -> {
//上线
return null;
};
Future future = fixedThreadPool.submit(task);
try {
future.get();
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new Exception("上线err");
} finally {
countDownLatch.countDown();
}
}