Java 中 Runnable 封装一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法。Callable 与 Runnable 类似,但是有返回值。Future 保存异步计算的结果。
Callable
Callable 接口是一个参数化的类型,只有一个方法 call。
public interface Callable<V> {
V call() throws Exception;
}
类型参数是返回值的类型。例如,Callable<Integer>
表示一个最终返回 Integer
对象的异步计算。
Future
Future 保存异步计算的结果。可以启动一个计算,将 Future 对象交给某个线程,然后忘掉它。Future 对象的所有者在结果计算好之后就可以获得它。
Future 接口具有如下方法:
public interface Future<V> {
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
}
第一个 get 方法的调用被阻塞,直到计算完成。如果在计算完成之前,第二个方法的调用超时,则拋出一个 TimeoutException
异常。如果运行该计算的线程被中断,两个方法都将拋出 InterruptedException
。如果计算已经完成,那么 get 方法立即返回。
如果计算还在进行,isDone 方法返回 false;如果完成了,则返回 true。
可以用 cancel 方法取消该计算。如果计算还没有开始,它被取消且不再开始。如果计算处于运行之中,那么如果 maylnterrupt 参数为 true,它就被中断。
FutureTask
FutureTask 同时实现了 Runnable 接口和 Future 接口,所以它既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值,FutureTask 包装器是一种非常便利的机制。
FutureTask 提供了 2 个构造器:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
示例
import java.util.concurrent.*;
public class KnowledgeDict {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<>(task);
executor.submit(futureTask);
executor.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程在执行任务");
try {
System.out.println("task 运行结果:" + futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕");
}
static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程在进行计算");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++)
sum += i;
return sum;
}
}
}