Future使用的这个坑你知道吗?
@Test
public void futureWithException() {
/* newFixedThreadPool等价于 return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());*/
// 核心线程数和最大线程数都是1
Future<Object> future = Executors.newFixedThreadPool(1).submit(() -> {
System.out.println("即将抛出异常");
throw new RuntimeException();
});
while (!future.isDone()) ;
}
// 没有异常抛出! 风平浪静!
咦? 我的RuntimeException()异常咧?
那怎么才能抛出异常呢!
我做了下测试,发现Future如果没有调用get()
方法不会抛出异常。
@Test
public void futureWithException() {
Future<Object> future = Executors.newFixedThreadPool(1).submit(() -> {
System.out.println("即将抛出异常");
throw new RuntimeException();
});
while (!future.isDone()) ;
try {
future.get();
} catch (Exception e) {
e.printStackTrace();
}
}
我不禁好奇? 为啥咧?
经过我深入观察发现: 其实这个不是Future的锅,是java.util.concurrent.ThreadPoolExecutor
的问题!
ThreadPoolExecutor
的submit使用了FutureTask:
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task); //此处返回FutureTask
execute(ftask);
return ftask;
}
让我们再看看FutureTask
的run方法(任务执行时就是取执行run()
方法)
public void run() {
...//其他代码
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;
// 如果call执行过程抛异常了; FutureTask的操作就是setException(ex)了事
setException(ex);
}
if (ran)
set(result);
}
} finally {
.....// 其他代码
}
}
深入看看setException()
方法
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { //使用CAS修改FutureTask的状态
outcome = t; // 异常记录到这里来了
//UNSAFE.putOrderedInt逻辑上等价于UNSAFE.putInt
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // 再设置为EXCEPTIONAL
// 信号相关的东西,暂时不管
finishCompletion();
}
}
至于为什么get()
会抛出异常:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) // EXCEPTIONAL>COMPLETING
s = awaitDone(false, 0L);
return report(s);
} // 好吧, 平平无奇, 让我们深入看看report()方法
private V report(int s) throws ExecutionException {
Object x = outcome; // 阿这,破案了。(还记得outcome吗,这个就是之前在run中设置的异常呀!)
if (s == NORMAL)
return (V) x; // 抛出异常
if (s >= CANCELLED)
throw new CancellationException();// 抛出异常
throw new ExecutionException((Throwable) x);// 抛出异常(包装下重新抛出)
}
而java.util.concurrent.FutureTask#get(long, java.util.concurrent.TimeUnit)
也会调用report
方法哦!
结案了!