ThreadPoolExcutor 线程池 异常处理 (下篇)
前言
因为这是之前面试的一个题目,所以印象比较深刻,前几天写了一篇文章:ThreadPoolExcutor 线程池 异常处理 (上篇) 中已经介绍了线程池异常的一些问题以及一步步分析了里面的一些源代码,今天就来继续说下如何防范这种情况。
结论
这里直接抛出结论,然后再一个个分析:
- 在我们提供的Runnable的run方法中捕获任务代码可能抛出的所有异常,包括未检测异常
- 使用ExecutorService.submit执行任务,利用返回的Future对象的get方法接收抛出的异常,然后进行处理
- 重写ThreadPoolExecutor.afterExecute方法,处理传递到afterExecute方法中的异常
- 为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常 (不推荐)
分析解读
Runnable的run方法中捕获任务代码可能抛出的所有异常
这个其实最简单,但是往往面试官问这个问题 考察的点也不在这里。具体的方式可以参考我之前的一篇文章:论如何优雅的自定义ThreadPoolExecutor线程池
核心代码如下:
使用ExecutorService.submit执行任务,利用返回的Future对象的get方法接收抛出的异常
1, 使用submit执行异步任务,然后通过Future的get方法来接收异常。演示如下: 冲图片可以看到,使用了get方法后,这里直接接收到了异常信息。
2, 这里newTaskFor返回的是FutureTask,然后传递给了execute方法:
3, 接着我们继续往下跟踪execute方法,发现这里调用的是ThreadExecutor中的execute方法,在ThreadPoolExcutor 线程池 异常处理 (上篇) 我们已经分析过这里,最终会到addWorker方法中执行线程的start()方法,因为我们在上一张图片传递的是FutureTask, 所以我们继续跟踪FutureTask中的run方法:
4, 到了FutureTask.run() 方法中,一切似乎都已经明了,这里会有catch捕获当前线程抛出的异常,紧接着我们看看setException做了什么事情:
5,setExcetion首先是将一个异常信息赋值给一个全局变量outcome,并且将全局的任务状态state字段通过CAS更新为3(异常状态) 然后最后做一些清理工作。
6,finishCompletion后续是做一些线程池的清理工作,这里涉及到线程池以及线程池中的等待队列的操作,不清楚的同学可以看下线程池实现代码。到了这里线程池中的线程执行已经完毕了,下面再去跟踪一下FutureTask.get()方法。
7,这里是FutureTask.get()的底层实现,这里其实会拿上面的setException方法中设置的outcome和state做一些逻辑判断,到了这里就直接往上抛出了异常,所以我们在最开始的main方法中才能够捕获到这个异常。
重写ThreadPoolExecutor.afterExecute方法,处理传递到afterExecute方法中的异常
这里为何要重写afterExecute方法呢?因为线程执行完毕后一定会执行此方法,源码如下:
所以我们可以重写此方法来达到接收异常的目的。
为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常 (不推荐)
1,我们在之前ThreadExecutor->Worker->run方法中直接往上抛出了异常,但是这些异常抛到哪里了呢?
2,通过查询JVM的一些资料,最终的异常会到Thread.dispatchUncaughtException中,如下图:
3,所以当我们自定义UncaughtExceptionHandler时就可以捕获到
具体测试代码如下:
/**
* 测试singleThreadPool异常问题
*
* @author: wangmeng
* @date: 2019/3/25 23:40
*/
public class ThreadPoolException {
private final static Logger LOGGER = LoggerFactory.getLogger(ThreadPoolException.class);
public static void main(String[] args) throws InterruptedException {
ExecutorService execute = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactoryBuilder().setUncaughtExceptionHandler(new MyHandler()).build());
execute.execute(new Runnable() {
@Override
public void run() {
LOGGER.info("=====11=======");
}
});
TimeUnit.SECONDS.sleep(5);
execute.execute(new Run1());
}
private static class Run1 implements Runnable {
@Override
public void run() {
int count = 0;
while (true) {
count++;
LOGGER.info("-------222-------------{}", count);
if (count == 10) {
System.out.println(1 / 0);
try {
} catch (Exception e) {
LOGGER.error("Exception",e);
}
}
if (count == 20) {
LOGGER.info("count={}", count);
break;
}
}
}
}
}
class MyHandler implements Thread.UncaughtExceptionHandler {
private final static Logger LOGGER = LoggerFactory.getLogger(MyHandler.class);
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.error("threadId = {}, threadName = {}, ex = {}", t.getId(), t.getName(), e.getMessage());
}
}
上面说了其实是不推荐重写UncaughtExceptionHandler 的,因为UncaughtExceptionHandler 只有在execute.execute()
方法中才生效,在execute.submit
中是无法捕获到异常的。
由于本人水平有限,文章中如果有不严谨的地方还请提出来,愿闻其详。
- Silverlight:针式打印机文字模糊的改善办法
- 大数据和云计算技术周报:NoSQL特辑
- 常用业务接口界面化 in python flask
- 打印机设置(PrintDialog)、页面设置(PageSetupDialog) 及 RDLC报表如何选择指定打印机
- 区块链推动支付革命
- MySQL常见的库操作,表操作,数据操作集锦及一些注意事项
- nohup命令
- 跨浏览器的剪贴板访问解决方案
- 装逼必备:大型分布式网站术语分析
- 年前爆炸一波!小程序视频功能来了!
- ubuntu13.04环境hadoop1.2.1单机模式安装
- silverlight:telerik RadControls中RadGridView的一个Bug及解决办法
- scope引起的问题
- JS正则表达式常用函数汇总
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 制作的 dotnet tool 运行失败提示依赖缺失
- 撸了个多线程断点续传下载器,我从中学习到了这些知识
- 使用 IOC 控制反转和 DI 依赖注入的意义
- Servlet配置初始值,并获取初始值
- CSS 技巧一则 -- 不定宽溢出文本适配滚动
- 潘石屹用Python解决100个问题 | 最大公约数
- 我的天上传文件又出现问题了(超出大小限制)
- 现在学 PHP 没有发展?来看看这个后台框架你还会这么想吗
- leetcode-easy-array-最大子序和
- Leecode No.3 无重复字符的最长子串
- Solidity 0.6.11 更新
- 潘石屹用Python解决100个问题 | 最小公倍数
- 通过CREATE2获得合约地址:解决交易所充值账号问题
- 小知识:如何判定crontab任务的执行频度
- 以太坊合约静态分析工具Slither简介与使用