在线程池中,如果一个线程抛出一个线程,只有调用future.get方法的时候,我们才知道,否则就什么都不知道了。
如果一个线程抛异常了,为什么我们调用get的时候可以知道呢?这是future将子线程内部的异常给封装起来了。
我们来看代码ThreadPoolExecutor的submit方法(实现在父类AbstractExecutorService中):
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
可以看到这个类返回了一个RunnableFuture,不过RunnableFuture是一个接口,这里的实现类我们要看代码 newTaskFor()这个方法:
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
这里我们可以看到new了一个FutureTask,那么我们可以看一下FutureTask这个类,这个类从名字上看,是一个Future, 也是一个Task, 是Future,则有get方法和其他的一些方法, 是Task这有run方法。
我们先看get方法吧:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
从上面的代码中可以看到,如果任务没有执行完成,则需要等待,如果执行完成了,则就调用report方法
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
我们看到,这里最后会抛出ExecutionException,这个异常将x包装了一下。这里看到x即可以是一个正常的返回值,也可以是一个异常。
那我们需要看一下这个x赋值的地方,简单猜想一下,这个异常应该是在run方法中的catch部分中获得的,下面我们看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);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
这里我们很容易找到catch部分,setExeception:
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
看到这里,我们就知道了,出异常是将throwable 给到outcome,get的时候,就将outcome封装成ExecutionException并抛出。