华为|有返回结果的异步任务Futrue与Callable

华为|有返回结果的异步任务Futrue与Callable

文章图片

华为|有返回结果的异步任务Futrue与Callable

上一篇讲解了线程池execute方法详细执行过程 , execute接受Runnable参数但没有返回执行结果 , 在需要有执行结果的地方就不太适用了 。
异步任务有返回值如果出现一个计算量稍大用时较长而后面需要结果的情况 , 可以先把这个任务提交到另外的线程异步执行 , 主线程仍然继续做运行等到需要计算结果的时候再来获取 , 但是提供异步计算Runnable或者说线程Thread都不返回结果 。
但Java中提供了Future与Callable来实现可以获取异步执行结果的一套框架 , 在上一篇文章中线程池的execute方法没有返回值 , 但是线程池提供的submit方法支持返回值 , 而submit利用Future与Callable实现 。
Future与Callable简单解释Callable接口声明了一个方法call提供一个泛型的返回结果 ,
Future接口声明了5个接口:
cancel方法:取消任务 , 如果任务取消成功则返回true , 如果任务取消失败则返回false 。 参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务 , 如果设置true , 则表示可以取消正在执行过程中的任务 。 如果任务已经完成 , 则都返回false 。 ; 。
isCancelled方法:表示任务是否已经被取消 。
isDone方法:表示任务是否已经完成 , 若任务完成 , 则返回true;
get()方法:用来获取任务结果 , 这个方法会阻塞线程 , 会一直等到任务执行完毕才返回;get(long timeout TimeUnit unit)方法如果在指定时间内 , 还没获取到结果 , 就直接返回null 。
再看线程池的submit线程池提供了三个submit都继承至AbstractExecutorService的实现 , 源码如下图:

三个方法接受不同的参数 , 但都会作为newTaskFor方法的参数生成一个RunnableFuture类的对象 , 所以最关键在于RunnableFuture类 。
而RunnableFuture也简单是一个接口并且继承了Runnable与Future , 所以它可以被当作Runnable用在线程池的execute方法中 , 而同时又拥有Future的所有功能 。
而submit通过newTaskFor生成的实际上是RunnableFuture子类 , 通过看源码实际上生成的是RunnableFuture的实现类FutureTask , 最终的关键就是FutureTask类 。
最关键类FutureTask详解FutureTask实现RunnableFuture , RunnableFuture继承Runnable与Future , 所以可以当作Runnable在线程池中使用 , 作为execute方法的参数 , 同时它也是Future的真正实现者 , 它的关键属性如下图:

前面提到的Future几个方法在FutureTask中得到了实现 , 实现也很简单 , 源码如上图FutureTask维护一个状态state , 来表示任务状态取消、中断、完成等 。
【华为|有返回结果的异步任务Futrue与Callable】我们都是通过Future的get方法来获取结果 , 而FutureTask对get方法实现也简单:
首先判断state的状态 , 如果大于3则抛出异常;
等于3则说明已经完成会执行Thread.yield()让出CPU;
小于3则说明任务还没有完成 , 就会把线程组装成WaitNode加入到waiters中 , 并中断当前线程;
从上一篇知道execute最终是执行参数的run方法 , 在这里也就是FutureTask的run方法执行过程如下:
首先执行callable的call方法获取到返回结果;
CAS方式把状态从0设置为1;
如果设置成功(保证线程安全)把结果赋值给outcome;
第四步把状态从1设置为2 , 表示完成;
最后唤醒waiters链表中的线程 。
总结可以看到有返回结果的异步实现最终依赖FutureTask , 它同时实现了Runnable与Future , 拥有一个Callable属性 。 get方法根据FutureTask的状态会把线程挂起并放到等待链表中了 。
同时它可以被用到线程池中被执行 , 在线程池中最后会调用它的run方法 , run方法会调用Callable的call方法也就是真正计算的方法 , 返回结果后会修改FutureTask的状态并唤醒等待链表的线程 。
线程池的submit方法还支持Runnable参数 , 但是FutureTask执行的是Callable的call方法 , 那么Runnable中的run是怎么转换成Callable的call方法呢?
实际上也很简单 , 采用适配器模式 , 建一个Callable的子类RunnableAdapter , 它保存一个Runnable属性 , RunnableAdapter的call方法调用Runnable的run方法 , 至于返回的结果为null或者自己传一个结果 。
所以要实现有返回结果的异步任务 , 要么实现Callable并实现call方法 , 或者创建一个Runnable的实现类也行 。
Java程序员日常学习笔记 , 如理解有误欢迎各位交流讨论!


    推荐阅读