Callable, Future和FutureTask简述

JDK1.5后新增了一些接口用于并发编程的接口,Callable,Future。以及1.6添加了FutureTask。之前看到了他们的使用,JDK1.8还添加了CompletableFuture,一直想了解一下这些东西。这里记录下对Callable, Future和FutureTask的理解。

相关接口

FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable,Future接口。FutureTask又有一个Callable参数的构造函数,下面分别介绍这些接口。

Runnable

这个是在Java一开始就出现了的接口,只有一个接口方法run,表示可以被运行的类。可以放到线程池,Thread中运行。跟Thread是两种不同的实现方式。

Future

这是在JDK1.5新增的接口,展示异步执行的结果(结果,将来的,Future)。他有以下几个方法:

  • cancel: 尝试去取消这个任务,如果该任务已经完成,或者已经取消,或者其他什么原因将不能取消。
  • get: 可以通过get方法获取异步线程的结果,如果没有执行完成则阻塞当前线程,等待异步线程执行完获取结果。
  • isDone: 判断该任务是否已经完成。如果是被取消,中断等,该方法也会返回true。
  • isCancelled: 判断该任务是否已经取消。

Runnable和Thread都有一个问题,就是不能够直接获取异步线程运行的结果,而Future可以做到。

RunnableFuture

实现了Runnable接口和Future接口。

Callable

一个可以返回结果的接口。只有一个方法call。

使用范例

使用Callable放到线程池中运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class TestFuture {
public static void main(String[] args) throws InterruptedException, ExecutionException{
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> future = executor.submit(task);
// do something
System.out.println("可以先做一些事情,再去获取运行结果");
if(!future.isDone()){
// you also can do something.
}
System.out.println("运行结果: "+future.get()); // 可能会阻塞等待结果
}
static class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("异步任务开始运行");
TimeUnit.SECONDS.sleep(3);
int sum = 0 ;
for(int i = 0 ; i != 200; ++i){
sum+=i;
}
System.out.println("异步任务完成运行");
return sum;
}
}
}
//运行结果
/*
可以先做一些事情,再去获取运行结果
异步任务开始运行
异步任务完成运行
运行结果: 19900
*/

可以自己实现Callable接口,Executor执行完成后会返回一个Future对象。在获取运行结果前,可以先做一些其他的事情。

使用FutureTask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TestFutureTask {
public static void main(String[] args) throws InterruptedException, ExecutionException{
ExecutorService executor = Executors.newCachedThreadPool();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Task());
executor.submit(futureTask); //调用的submit(Runnable runnable) 方法
// do something
System.out.println("可以先做一些事情,再去获取运行结果");
if(!futureTask.isDone()){
// you also can do something.
}
System.out.println("运行结果: "+futureTask.get()); // 可能会阻塞等待结果
}
static class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("异步任务开始运行");
TimeUnit.SECONDS.sleep(3);
int sum = 0 ;
for(int i = 0 ; i != 200; ++i){
sum+=i;
}
System.out.println("异步任务完成运行");
return sum;
}
}
}

跟前面直接使用Callable结果是一样的,只是使用方式有点不一样。这里不需要从submit中获得Future对象,直接就是传入的那个参数futureTask,在有些使用使用起来会更加方便。

总结

我觉得Future接口提供了一种异步操作新的思路,Runnable和Thread都是直接执行完就没有了,并没有获得执行结果的接口,但是Future可以获得执行结果。Future就是用来展示执行结果。当然Future这一系列接口的使用远远不止这些,Future的get方法还可以设置超时时间。