前言
分析前几天遇到的一个老代码留下的坑。线程池中运行Callable
线程时抛出的异常捕获不到,简化的逻辑如图,环境是jdk8:
运行结果:
解决方案
- 线程池返回
Future<>
,调用其get()
- 在Callable中 try-catch可能抛错的异常
运行结果:源码分析
不难发现线程池提交时创建的类为FutureTask
。1
2
3
4
5
6
7
8
9
10public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
看FutureTask.run()
之前,先简单结束一下其关的属性。
- state:线程的状态。主要有如下几种:
- NEW: 新建
- COMPLETING: 运行在
- NORMAL: 正常完成
- EXCEPTIONAL: 异常
- CANCELLED: 取消
- INTERRUPTING: 被中断的中间状态
- INTERRUPTED: 被中断的最终状态
- outcome: get()返回值
1 | public void run() { |
注意这里
1 | try { |
这里线程在运行时抛出异常时,FutureTask
把异常信息赋值给outcome
,并将state
设为EXCEPTIONAL
。
1 | public V get() throws InterruptedException, ExecutionException { |
在调用get()时,如果运行时抛出异常,此时会抛出异常。
总结
这种坑还是代码规范的问题。Callable
返回结果并没有被使用可以用Runnable
代替;try-catch代码的习惯。