Java 中异步任务的实现 之 Callable,Future,FutureTask
这里,我们主要用到的类和接口为:Callable,Future,FutureTask
Runable只需关心运行的动作行为,而Callable同时关心运行的结果。
package java.util.concurrent;
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Future表示一个任务的生命周期。提供接口来判断任务完成,取消与否,也提供接口来获取任务结果。
get
方法的行为取决于任务的状态(尚未开始,正在运行,已完成)
。
- 如果任务
已经完成
,那么get会立即返回结果或者抛出异常(任务执行过程中发生异常、任务被取消也属于任务已完成
) - 如果任务
没有完成
,get将阻塞。 - 如果任务
抛出异常
,那么get会将该异常封装为ExecutionException
重新抛出,可以通过getCause
来获取最初的异常 - 如果任务
被取消
,那么get将抛出CancellationException
package 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;
}
可以通过多种方法来创建一个Future任务
。
ExecutorService中所有submit
方法都返回一个Future。- 指定
Runnable或者Callable实例化一个FutureTask
(FutureTask实现了Runnable,因此可以将它提交给Executor来执行,或者直接调用它的run方法)
我们简单看看FutureTask的代码:
public class FutureTask<V> implements RunnableFuture<V> {
// 这里不列出它的代码了
}
// 其中RunnableFuture接口:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
下面来测试一个Future
代码例子,体验一下异步任务:
@Slf4j
public class FutureExample {
/**
* 定义自己的Callable实现
* 它描述了任务怎么执行,以及执行的返回结果
*/
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
log.info("do something in callable");
// 异步任务执行5秒钟
Thread.sleep(5000);
return "Done";
}
}
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交Callable到ExecutorService,会返回一个Future
Future<String> future = executorService.submit(new MyCallable());
log.info("do something in main");
// 主任务提交任务后执行1秒钟,然后通过get等待执行结果
Thread.sleep(1000);
String result = future.get();
log.info("result:{}", result);
}
}
// 顺带讨论一下Lambda表达式怎么简化程序代码:
@Slf4j
public class FutureExample {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit( ()->{ // Callable
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
});
log.info("do something in main");
Thread.sleep(1000);
String result = future.get();
log.info("result:{}", result);
}
}
我们运行,看一看运行结果如下,可以看到,43秒开始等待运行结果,5秒后的48秒返回结果"Done"
11:46:43.086 [pool-1-thread-1] INFO FutureExample - do something in callable
11:46:43.086 [main] INFO FutureExample - do something in main
11:46:48.092 [main] INFO FutureExample - result:Done
同样,我们使用FutureTask
也能达到同样的效果:
@Slf4j
public class FutureTaskExample {
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
}
});
new Thread(futureTask).start();
log.info("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
log.info("result:{}", result);
}
}
// 同样,我们尝试Lambda表达式重写一下:
@Slf4j
public class FutureTaskExample {
public static void main(String[] args) throws Exception {
FutureTask<String> futureTask = new FutureTask<>(()->{
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
});
new Thread(futureTask).start();
log.info("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
log.info("result:{}", result);
}
}