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 的返回值。
我们通过一个线程运行 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.................