1 为什么需要Callable和Future
Runnable没有返回值,也不抛异常,这样主线程不能知道子线程的执行结果。
为了解决这个问题就有了Callable和Future。Callable提供的call方法返回一个结果,然后把这个结果交给Future,主线程通过Future就能够获取子线程执行的结果了。
Callable给子线程提供入参,Future提供子线程的返回值。FutureTask合二为一,它把Callable作为成员保存起来,而它本身就是一个Future。
2 什么是Future
Future被用来表示一个异步计算的未来结果。
Future提供了5个接口来查询异步任务的计算结果:
isDone:是否执行完了。
两个get,一个阻塞,一个计时阻塞。
cancel,取消任务的执行。
isCanceled,查询任务是否取消了执行。
3 Future有什么用
3.1 异步调用和并发
耗时的计算适用于异步和Future。
3.2 Future异步计算的使用举例
计算密集的任务;
处理大的数据结构;
远程方法调用:下载文件、web服务等
4 Future是如何实现的
Future能实现本质上是因为java的对象都是放在堆上的。FutureTask是在主线程中创建的对象,将之交给其它线程或者线程池去执行,线程执行完了之后,有一个返回值,将该返回值放在FutureTask的成员outcome上。然后,FutureTask再提供get函数来给主线程获取这个执行的结果。
4.1 FutureTask get实现的原理
第一,维护一个状态机,用户调用get之后,根据不同的执行状态,返回不同的值。
第二,如果任务还没有执行完,就把该线程放入等待队列,并且调用LockSupport.park()函数让阻塞该线程,这样get就阻塞了。
第三,在任务执行完了之后,会把这些等待队列中的线程踢出,并且调用LockSupport.unpark()函数解除阻塞,线程被唤醒。
5 Runnable和Callable比较
5.1 相同点
Runnable和Callable都提供线程的执行体。
5.2 Runable不提供返回值,也不抛异常。Callable提供返回值,可能会抛一个异常
比如FutureTask实现了Callable接口,call方法执行了之后将结果放在FutureTask的outcome成员中。
调用get的时候,如果执行体正常执行则返回该outcome,如果不正常执行就返回Exception。
6 Callable的使用场景
6.1 和Thread一起使用
需要一个中间人FutureTask,该类实现了Runnable和Future接口。因为实现了Runnable接口,因此可以作为参数传给Thread,创建一个新的thread。
FutureTask也可以作为参数传给线程池。
7 总结一下
Callable和Future一起使用来获取异步执行的结果。Callable作为入参,提供线程的执行体和该异步计算的返回值。Future作为异步计算的返回结果。
8 grpc中对Future的使用,grpc是如何通过Future来实现对异步调用结果的获取的?
异步调用时,服务器端调用执行完了,执行的结果是如何交给客户端的?
io是netty实现的,服务器端的调用执行之后,通过通道告知客户端,然后客户端就调用相应的listener。netty的服务器端和客户端都是有多路复用器的。用于监听各个IO事件。
10 参考资料
10.1 https://www.baeldung.com/java-future
10.2 https://www.geeksforgeeks.org/callable-future-java/
10.3 https://blog.csdn.net/codershamo/article/details/51901057