Callable、Future 和 FutureTask
启动线程执行任务,如果需要在任务执行完毕之后得到任务执行结果,可以使用从 Java 1.5 开始提供的 Callable 和 Future。
下面分析一下 Callable、Future 以及 FutureTask 的具体实现及使用方法。
Callable 与 Runnable
Runnable 是一个接口,在它里面只声明了一个run()方法:
public interface Runnable {
public abstract void run();
}
由于 run() 方法返回值为 void 类型,所以在执行完任务之后无法返回任何结果。
Callable 位于 java.util.concurrent 包下,它也是一个接口,在它里面也只声明了一个 call() 方法类似于,java.lang.Runnable 的 run() 方法,实现 Callable 接口的类和实现 Runnable 接口的类都是可以被其它线程执行的任务。
public interface Callable<V> {
V call() throws Exception;
}
可以看到 call() 方法是有返回值的,可以将执行的结果返回。
Callable 和 Runnable的区别:
1、Callable 中定义的是 call() 方法,Runnable 中定义的是 run() 方法。
2、Callable 中的 call() 方法可以返回执行任务后的结果,Runnable 中的 run() 方法无法获得返回值。
3、Callable 中的 call() 方法定义了 throws Exception 抛出异常,抛出的异常可以在主线程 Future.get() 时被主线程捕获;Runnable 中的 run() 方法没有定义抛出异常,运行任务时发生异常时也会上抛,因为即使不加默认也会上抛 RuntimeException,但异常无法被主线程获取。
4、运行 Callable 任务可以拿到一个 Future 对象代表异步运算的结果。
Future
Future 就是对于具体的 Runnable 或者 Callable 任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过 get 方法获取执行结果,该方法会阻塞直到任务返回结果。
Future 类位于 java.util.concurrent 包下,它是一个接口:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
- get() :用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- V get(Long timeout , TimeUnit unit) :获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的 timeout 时间,该方法将抛出异常。
- boolean isDone() :如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
- boolean isCanceller() :如果任务完成前被取消,则返回 true。
- boolean cancel(boolean mayInterruptRunning) :尝试取消任务的执行,取消成功返回 true,取消失败返回 false,参数mayInterruptIfRunning 表示是否允许中断正在执行的任务
因为 Future 只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的 FutureTask。
FutureTask
FutureTask 的实现:
public class FutureTask<V> implements RunnableFuture<V>
FutureTask 类实现了 RunnableFuture 接口,我们看一下 RunnableFuture 接口的实现:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
可以看出 RunnableFuture 继承了 Runnable 接口和 Future 接口,而 FutureTask实现了 RunnableFuture 接口。所以它既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值。
![](https://img.haomeiwen.com/i2079881/bc69f9ce8edb0f10.png)
我们通过一个线程运行 Callable,但是 Thread 不支持构造方法中传递 Callable 的实例,所以我们需要通过 FutureTask 把一个 Callable 包装成 Runnable, 然后再通过这个 FutureTask 拿到 Callable 运行后的返回值。
要 new 一个 FutureTask 的实例,有两种方法:
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
FutureTask 是Future 接口的一个唯一实现类
Future和FutureTask的使用
/**
* 类说明:演示Future等的使用
*/
public class UseFuture {
/*实现Callable接口,允许有返回值*/
private static class UseCallable implements Callable<Integer> {
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算!");
// Thread.sleep(1000);
for (int i = 0; i < 5000; i++) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("Callable子线程计算任务中断!");
return null;
}
sum = sum + i;
// System.out.println("sum=" + sum);
}
System.out.println("Callable子线程计算结束!结果为: " + sum);
return sum;
}
}
public static void main(String[] args)
throws InterruptedException, ExecutionException {
UseCallable useCallable = new UseCallable();
//包装
FutureTask<Integer> futureTask = new FutureTask<>(useCallable);
Random r = new Random();
new Thread(futureTask).start();
Thread.sleep(1);
if (r.nextInt(100) > 50) {
System.out.println("Get UseCallable result = " + futureTask.get());
} else {
System.out.println("Cancel................. ");
futureTask.cancel(true);
}
}
}
打印结果可能以下几种:
Callable子线程开始计算!
Cancel.................
Callable子线程计算任务中断!
Callable子线程开始计算!
Callable子线程计算结束!结果为: 12497500
Get UseCallable result = 12497500
Callable子线程开始计算!
Callable子线程计算结束!结果为: 12497500
Cancel.................