如果我们是使用submit提交的任务,那么就要注意异常的处理了。
先来看一段代码:
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(() -> {
System.out.println("任务开始执行...");
int a = 10 / 0; // 抛出异常
});
executorService.shutdown();
很明显,我们提交的任务会抛出异常,但实际上,控制台并不会打印任何异常的信息。那异常到底被谁吃了?
那我们就需要看看我们提交任务后,线程池做了什么手脚了,在AbstractExecutorService类中:
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task); //该方法会返回一个FutureTask
// 狸猫换太子了,execute执行的任务是处理过的
execute(ftask);
return ftask;
}
// 实际上就是对我们的任务又进一步封装了
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
可以看到,我们提交的task会被封装成一个FutureTask,最后交给线程池执行的实际上是这个FutureTask。FutureTask的结构如下:
可以看出FutureTask也实现了Runnable接口,所以到时候FutureTask的run()方法就会被调用:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 在这里才会真正的去执行我们提交的任务
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); //将异常信息塞到outcome中
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t; // 将异常信息设置到outcome中
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
看到没有,FutureTask使用try-catch捕获了任务执行的异常,并通过setException方法将异常塞到了outcome中,因此我们的异常被吃了,破案了。
那怎么拿到我们的异常信息?FutureTask的get方法会返回outcome,因此调用FutureTask的get方法就可以拿到异常信息。
ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<Integer> future = executorService.submit(() -> {
System.out.println("任务开始执行...");
int a = 10 / 0;
return a;
});
future.get(); //调用get方法
System.out.println("任务执行结束...");
executorService.shutdown();
当然,知道了异常是怎么被吃掉的,我们就可以自己捕捉异常。
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(() -> {
// 自己又try-catch捕获异常
try {
System.out.println("任务开始执行...");
int a = 10 / 0;
return a;
}catch (Exception e){
System.out.println(e);
}
return null;
});
对于execute()方式执行的任务,虽然异常不会被吃掉,但依旧推荐我们自己try-catch做一下异常处理