文章目录
- 一、Future 的应用场景
- 二、Future 的类图结构
- 三、Future 的接口方法
- 四、Future 的应用示例
一、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