一、Future 的应用场景
-
在并发编程中,我们经常需要用非阻塞的模型,
Java
默认多线程的三种实现中,继承Thread
类和实现Runnable
接口是异步并且主调函数是无法获取到返回值的。通过实现Callback
接口,并用Future
可以来接收多线程的执行结果 -
Future
接收一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callable
以便任务执行成功或失败后作出相应的操作 -
采用
Future
修改的异步方法,在每次被异步调用以后会马上返回(无论一步方法体是否执行完成),Future
就会监听异步任务执行状态(成功、失败),等到执行完成以后,就能通过Future.get()
方法获取到异步返回的结果 -
也就是说,如果批量调用采用
Future
修饰的异步方法,程序不会阻塞等待,然后再遍历Future
列表,即可获取到所有的异步结果(Future
的内部机制是等所有的异步任务完成了才进行遍历), 这种请求耗时只会略大于耗时最长的一个Future
修饰的方法
二、Future 的类图结构
-
RunnableFuture
-
RunnableFuture
接口同时继承了Future
接口和Runnable
接口,在成功执行完成run()
以后,可以通过Future
访问执行结果 -
RunnableFuture
接口的实现类是FutureTask
,FutureTask
是一个可取消的异步计算,FutureTask
类提供了Future
的基本实现,(取消、判断是否取消、判断是否完成、获取异步结果(阻塞)、有限时间获取异步结果(阻塞)) -
FutureTask
能用来包装一个Callable
或Runnable
对象,因为它实现了Runnable
接口,而且它能被传递到Executor
进行执行,为了提供单例类,这个类再创建自定义的工作类时提供了protected
构造函数
-
-
SchedualFuture
SchedualFuture
表示一个延时的行为可以被取消,通常一个安排好的Future
是定时任务SchedualedExecutorService
的结果
-
CompleteFuture
- 一个
Future
类是显示的完成,而且能被用作一个完成等级,通过它完成触发支持的依赖函数和行为。当两个或多个线程要执行完成或取消操作时,只有一个能够成功
- 一个
-
ForkJoinTask
- 基于任务的抽象类,可以通过
ForkJoinPool
来执行。一个ForkJoinTask
是类似于线程实体,但是相对于线程实体是轻量级的。大量的任务和子任务会被ForkJoinPool
池中的真实线程挂起来,以某些使用限制为代价
- 基于任务的抽象类,可以通过
三、Future 的接口方法
-
cancel(boolean mayInterruptIfRunning)
用来停止一个任务,如果任务可以被停止(通过mayInterruptIfRunning
来进行判断),则可以返回true
,如果任务已经完成、已经停止或者无法停止,则会返回false
-
isCancelled
用来判断当前方法是否取消 -
isDone
用来判断当前方法是否完成 -
get
当异步任务结束后返回一个结果,如果调用get()
时任务还没有结束,那么调用线程将会阻塞,直到有返回结果为止 -
get(long time, TimeUnit unit)
最多等待timeout
的时间就会返回结果,不管异步任务是否执行完成
四、Future 的应用示例
-
测试代码
public class C98_Future { public Future rice() { long start = System.currentTimeMillis(); try { System.out.println("线程:" + Thread.currentThread().getName() + "------> 开始做饭"); TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + "------> 完成做饭,耗时:" + (System.currentTimeMillis() - start) + " ms"); return null; } public Future<String> soup() { long start = System.currentTimeMillis(); try { System.out.println("线程:" + Thread.currentThread().getName() + "------> 开始煲汤"); TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + "------> 完成煲汤,耗时:" + (System.currentTimeMillis() - start) + " ms"); return null; } public static void main(String[] args) throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); // JOIN 方法阻塞等待 // Thread rice = new CookRice(); // rice.start(); // rice.join(); // // CookSoup soup = new CookSoup(); // soup.start(); // soup.join(); // Future 方式非阻塞执行,阻塞等待结果 Callable callable1 = ()-> {{ new C98_Future().rice(); return "Finish1"; } }; FutureTask<String> futureTask1 = new FutureTask<String>(callable1); new Thread(futureTask1).start(); Callable callable2 = ()-> {{ new C98_Future().soup(); return "Finish2"; } }; FutureTask<String> futureTask2 = new FutureTask<String>(callable2); new Thread(futureTask2).start(); System.out.println(futureTask1.get()); System.out.println(futureTask2.get()); System.out.println("全部准备完毕时间:" + (System.currentTimeMillis() - start) + " ms"); } } class CookRice extends Thread { @Override public void run() { new C98_Future().rice(); } } class CookSoup extends Thread { @Override public void run() { new C98_Future().soup(); } }
-
JOIN
方法阻塞等待结果线程:Thread-0------> 开始做饭 线程:Thread-0------> 完成做饭,耗时:2006 ms 线程:Thread-1------> 开始煲汤 线程:Thread-1------> 完成煲汤,耗时:5005 ms 全部准备完毕时间:7012 ms
-
Future
方式非阻塞执行,阻塞等待结果线程:Thread-0------> 开始做饭 线程:Thread-1------> 开始煲汤 线程:Thread-0------> 完成做饭,耗时:2005 ms Finish1 线程:Thread-1------> 完成煲汤,耗时:5005 ms Finish2 全部准备完毕时间:5055 ms