两种情况都和 future.get 方法有关,那我们就从这个方法的源码入手。
这个 Future 是一个接口:
而这个接口有非常多的实现类。我们找哪个实现类呢?
就是下面这个实现类:
java.util.concurrent.FutureTask
至于是怎么找到它的,你慢慢往后看就知道了。
先看看 FutureTask 的 get 方法:
get 方法的逻辑很简单,首先判断当前状态是否已完成,如果不是,则进入等待,如果是,则进入 report 方法。
一进 get 方法,我们就看到了 state 这个东西,这是 FutureTask 里面一个非常重要的东西:
所以,一目了然,一个任务的终态有四种:NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED。
而我们主要关心 NORMAL、EXCEPTIONAL。
所以再回头看看 get 方法:
如果当前状态是小于 COMPLEING 的。
也就是当前状态只能是 NEW 或者 COMPLEING,总之就是任务还没有完成。所以进入 awaitDone 方法。这个方法不是本文关心的地方,接着往下看。
程序能往下走,说明当前的状态肯定是下面圈起来的状态中的某一个:
这个方法是干啥的?
注解说的很清楚了:对于已经完成了的 task,返回其结果或者抛出异常。
这里面的逻辑就很简单了,把 outcome 变量赋值给 x 。
然后判断当前状态,如果是 NORMAL,即 2,说明正常完成,直接返回 x。
如果是大于等于 CANCELLED,即大于等于 4 ,即这几种状态,就抛出 CancellationException。
剩下的情况就抛出 ExecutionException。
而这个“剩下的情况”是什么情况?
不就只剩下一个 EXCEPTIONAL 的情况了。
所以,经过前面的描述,我们可以总结一下。
当 FutureTask 的 status 为 NORMAL 时正常返回结果,当 status 为 EXCEPTIONAL 时抛出异常。
而当终态为 NORMAL 或者 EXCEPTIONAL 时,按照注释描述,状态的流程只能是这样的:
那么到底是不是这样的呢?
这就需要我们去线程池里面验证一下了。
寻找答案-线程池
先回答上一节的一个问题:我怎么知道是看 Future 这个接口的 FutureTask 这个实现类的:
可以看到,FutureTask 的构造方法里面默认了状态为 NEW。
然后直接在 runWorker 方法的 task.run 方法处打上断点:
这个 task 是一个 FutureTask,所以 run 方法其实是 FutureTask 的 run 方法。
跟着断点进去之后,就是 FutureTask 的 run 方法:
答案都藏在这个方法里面。
java.util.concurrent.FutureTask#run
标号为 ① 的地方是执行我们的任务,call 的就是示例代码里面的 sayHi 方法。
如果 sayHi 方法没有捕获运行时异常,则会在标号为 ② 的这个 catch 里面被捕获。然后执行标号为 ② 的这个代码。
如果 sayHi 方法捕获了运行时异常,则会进入标号为 ③ 的这个逻辑里面。
我们分别看一下标号为 ② 和 ③ 的逻辑: