上一篇讲解了线程池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,来表示任务状态取消、中断、完成等。
我们都是通过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程序员日常学习笔记,如理解有误欢迎各位交流讨论!