前言
创建一个线程,最熟悉的做法是集成Thread
类或者实现Runnable
接口重新run()
。 但是这两种创建方式在一些场景比如想要获取线程的执行结果时却不那么好用。J.U.C
满足了这种需求。 今天说一下不得不知的Callable
、Future
和FutureTask
Callable
比对Runnable
接口与Callable
接口:
public interface Runnable {
public abstract void run();
}
public interface Callable<V> {
V call() throws Exception;
}
可以看到,Callable
接口代表一种能返回结果并可能引发异常的任务。 实现者只要实现call()
并返回任务结果即可。 Callable
接口类似于Runnable,但是,Runnable
不返回结果,也不能抛出被检查的异常。
一个好的事实是,无论是Callable
还是Runnable
,都可以很好的与Executor
框架结合使用:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
Future
future
,单从语义上就知道代表未来,实际上也是如此。 先看接口定义:
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
提供了5个方法:
- cancel(boolean mayInterruptIfRunning):尝试取消执行此任务。
- isCancelled():如果此任务在正常完成之前被取消,则返回 true 。
- isDone():如果任务已完成返回 true。
- get():一直阻塞直至获取执行结果。
- get(long timeout, TimeUnit unit):在指定时间内获取执行结果,获取不到抛出TimeoutException
FutureTask
FutureTask
实现了RunnableFuture
接口,看下RunnableFuture
接口:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
可以看到它即实现了Runnable
接口,也实现了Future
接口,因此FutureTask
可以作为Runnable
的实现, 也可以作为Future
来获取Callable
的返回。看其构造器便知如何选择:
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
推荐的使用方式
开发中建议使用线程池+Callable
返回Future
的方式来获取异步结果,最好给予超时时间:
ExecutorService es = Executors.newSingleThreadExecutor();
Future<Integer> future = es.submit(() -> {
try {
System.out.println("开始执行计算任务...");
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 100;
});
es.shutdown();
// Integer integer = future.get();
// System.out.println("get() 获取异步任务结果:" + integer);
Integer integer;
try {
integer = future.get(2, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println("调用超时,返回错误结果");
integer = -1;
}
System.out.println("get(timeout) 获取异步任务结果:" + integer);
}
返回的结果值:
开始执行计算任务...
调用超时,返回错误结果
get(timeout) 获取异步任务结果:-1