并发:Java的Future和FutureTask
Future类的作用
Future
类是异步思想的典型运用,主要用在一些需要执行耗时任务的场景,避免程序一直原地等待耗时任务执行完成,执行效率太低。具体来说是这样的:当我们执行某一耗时的任务时,可以将这个耗时任务交给一个子线程去异步执行,同时我们可以干点其他事情,不用傻傻等待耗时任务执行完成。等我们的事情干完后,我们再通过 Future 类获取到耗时任务的执行结果。这样一来,程序的执行效率就明显提高了。
这其实就是多线程中经典的 Future
模式,你可以将其看作是一种设计模式,核心思想是异步调用,主要用在多线程领域,并非 Java 语言独有。
在 Java
中,Future
类只是一个泛型接口,位于java.util.concurrent
包下,其中定义了 5 个方法,主要包括下面这 4 个功能:
- 取消任务;
- 判断任务是否被取消;
- 判断任务是否已经执行完成;
- 获取任务执行结果。
源码
// V 代表了Future执行的任务返回值的类型
public interface Future<V> {
// 取消任务执行
// 成功取消返回 true,否则返回 false
boolean cancel(boolean mayInterruptIfRunning);
// 判断任务是否被取消
boolean isCancelled();
// 判断任务是否已经执行完成
boolean isDone();
// 获取任务执行结果
V get() throws InterruptedException, ExecutionException;
// 指定时间内没有返回计算结果就抛出 TimeOutException 异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutExceptio
}
FutureTask的作用
FutureTask
是Future
的具体实现。
FutureTask
实现了RunnableFuture
接口。RunnableFuture
接口又同时继承了Future
和 Runnable
接口。所以FutureTask
既可以作为Runnable
被线程执行,又可以作为Future
得到Callable
的返回值,可以作为任务直接被线程执行。
构造函数
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
例子
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTaskTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
long starttime = System.currentTimeMillis();
//input2生成, 需要耗费3秒
FutureTask<Integer> input2_futuretask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return 5;
}
});
new Thread(input2_futuretask).start();
//input1生成,需要耗费2秒
FutureTask<Integer> input1_futuretask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 3;
}
});
new Thread(input1_futuretask).start();
Integer integer1 = input1_futuretask.get();
Integer integer2 = input2_futuretask.get();
System.out.println(algorithm(integer1, integer2));
long endtime = System.currentTimeMillis();
System.out.println("用时:" + String.valueOf(endtime - starttime)+"ms");
}
//这是我们要执行的算法
public static int algorithm(int input, int input2) {
return input + input2;
}
}
这里用时仅仅只有3013ms,“input2生成, 需要耗费3秒”,这一步占了主要时间。由此观之,整个过程是异步的(不然时间将会是>3+2=5s的)。
CompletableFuture类
Future
在实际使用过程中存在一些局限性,比如不支持异步任务的编排组合、获取计算结果的 get() 方法为阻塞调用。
Java 8 才被引入CompletableFuture
类可以解决Future
的这些缺陷。CompletableFuture
除了提供了更为好用和强大的 Future
特性之外,还提供了函数式编程、异步任务编排组合(可以将多个异步任务串联起来,组成一个完整的链式调用)等能力。
//programmer-club中的例子
List<CompletableFuture<Map<Long, List<SubjectLabelBO>>>> completableFutureList = categoryBOList.stream().map(category ->
CompletableFuture.supplyAsync(() -> getLabelBOList(category), labelThreadPool).collect(Collectors.toList());