获取任务执行结果之Future原理
由于在开发过程当中需要获取线程执行的结果,故使用到了Java并发工具包java.util.concurrent下的Future类,最终通过Future的get来获取到线程执行的结果。这里我们来简单解析下,Future的实现原理及使用方式。
Java提供了两个自定义线程的底层接口,一个是Runnable接口一个是Callable接口。
1 | public interface Runnable { |
通过比较可以看出,Runnable接口的run函数返回时为Void,而Callable的call函数返回值为V,也就是通过泛型传入的类型参数,由此也就为获取线程执行结果提供了实现的支持。线程的执行我们一般通过交由线程池ThreadPoolExecutor来完成。在该类中,我们有多种提交任务的方式,如
1 | public void execute(Runnable command)(...) |
在ThreadPoolExecutor的父类AbstractExecutorService中,还提供了几个提交任务的函数
1 | public Future<?> submit(Runnable task) { |
这三个函数中,都通过将Runable或Callable包装成RunnableFuture,并交由线程池来执行,那么来看下RunnableFuture的定义
1 | public interface RunnableFuture<V> extends Runnable, Future<V> { |
该接口提供了两个功能,一个是继承Runnable接口提供了任务的运行功能,一个是继承Future接口,用于获取执行结果。接下来我们看下该处使用的RunnableFuture的具体实现类
1 | /** |
对应Runnable和Callable入参,创建FutureTask。其中由于Runnable是没有返回值的,由第一个函数的javadoc可知,我们可以传递一个T类型的value值,当任务执行完毕时,将会返回该value。
由此,我们大概可以推测出能获取执行结果的任务是如何执行的。首先,通过ThreadPoolExecutor的submit函数提交一个实现了Callable接口或者Runnable接口的实现类给线程池,线程池会将该任务包装成一个RunnableFuture对象(实现了Future接口及Runnable接口),并将该对象提交线程池执行,并返回该对象。在最外层,就可以通过Future的get()来获取线程的执行结果了。如下:
1 | PdfReaderCallable pdfReaderCallable = new PdfReaderCallable(url); |
其中PdfReaderCallable实现了Callable接口,pdfReadExecutor为线程池实现类。当执行该逻辑的线程执行了到第三条语句的时候,将被阻塞,一直等到task.get()返回结果才会继续向下执行。
接下来,让我们更深入地来看一下最终提交给线程池执行的FutureTask的实现原理。
1 | public class FutureTask<V> implements RunnableFuture<V> { |
这里只列举了几个比较重要的成员变量及成员函数。通过构造函数可以看出,成员变量callable是实际的任务类。在FutureTask的run方法中最终调用了该实际任务类来执行任务并获取结果result,最后调用set函数将result赋值给outcome对象。而在外部,我们通过get()函数来获取结果,如果任务含没有执行完,那么将会通过awaitDone函数阻塞,一直等待任务执行完毕,才能继续执行并最终通过report函数返回执行结果。
至此,获取任务执行结果的Future实现就分析结束。
欢迎关注个人公众号:
