1、Future前言
Future设计模式从本质上是用来解决的根本的问题在于可以让一个任务在异步执行,而任务执行完之后我们可以在未来的某一个时间点上去获取到这个任务执行的结果,不管这个任务是执行失败或成功
举个例子
比如我们要完成一件任务,正常情况是要等到这个任务执行完之后才能拿到任务执行的结果【单线程顺序执行逻辑】,而如果使用Future模式之后,则执行任务可以交由别的线程来执行,而我们可继续往下执行自己的事件,当在未来的某个时间点又能拿到这个任务的结果,其实也就是并行的方式。可能有的人说这不就是线程嘛,是的,但是普通的线程我们要等其执行完不得使用join()么,但是它会阻塞我们的继续往下执行。
2、Future官方文档
一个Future代表了一个异步计算的结果,它提供了很多方法用来检查此计算是否完成,或者等待它的完成,以及获取计算的结果。只有当计算结果后才可以使用get()方法来获取结果,必要时会进行阻塞直到任务执行结束。可以通过cancel()方法对其任务进行取消,此外还提供了其他方法来确定任务是正常完成还是说被取消了。
核心方法
- 1、boolean cancel(boolean mayInterruptIfRunning)
尝试取消此任务 - 2、boolean isCancelled()
如果此任务在正常完成之前被取消,则返回 true 。 - 3、boolean isDone()
是否任务执行完了 - 4、V get()
获取任务执行结果,如果任务没有完成,这个方法则会一直等待,没有超时机制 - 5、V get(long timeout, TimeUnit unit)
获取任务执行结果,如果任务没有完成,这个方法则会一直等待,直到超过限定时间,超过限定时间则会抛出异常
3、FutureTask
对于Future而言它是一个接口,要使用还得用具体实现类,FutureTask就是Future的一个具体实现类之一,比较常见,观察FutureTask实现的接口,就知道它有什么样的能力

瞅一下RunnableFuture呗:

接下来再来看一下FutureTask的重要方法:
两种构造方法
方法1
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
方法2
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
对于上面两个构造的使用场景其实也很好理解
- 当我们需要有返回结果当然就使用Callable的构造
- 当我们想要执行一个任务但是不需要得到返回结果,那么用Runnable的构造,其中方法二中传入Runnable方法最终会转换为使用Callable的构造方法
看一下Callable接口的定义

举例子观察FutureTask的使用
package com.concurrency2;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyTest5 {
public static void main(String[] args) {
Callable<Integer> callable = () -> {
System.out.println("子任务执行中");
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
int randomNumber = new Random().nextInt(500);
System.out.println("子任务执行中");
return randomNumber;
};
FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
new Thread(futureTask).start();
System.out.println("主任务执行中");
try {
System.out.println("task result:" + futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主任务已完成");
}
}
输出
主任务执行中
子任务执行中
子任务执行中
task result:177
主任务已完成
分析:
new Thread(futureTask).start();执行的是开辟一个子任务,执行的是callable的内容并要求返回Integer类型的结果,而callable的内容作为变量传进FutureTask中。因此现在就有两个线程一起执行,一个是主任务线程,一个是子任务线程,而后面主任务线程需要拿到子任务线程执行完的结果,因此一定要执行完futureTask.get()方法时,主任务线程才会继续执行下去,否则会一直进入等待状态(如果get()方法是带时间参数的方法,若超过的指定的时间则主任务线程会抛出异常,而子任务线程不会抛出异常,因为和子任务线程无关)
Future模式,它有一个最大的缺点就是调用get()方法来获取其任务的结果时会阻塞,还有一个CompletableFuture的实现类,弥补Future的不足,使得任务执行时和结果获取时都不阻塞,这里不做详细总结,具体看这里https://mp.weixin.qq.com/s/cu09f_LD4SlywZMTCBeokA
网友评论