Future 接口和实现 Future 接口的 FutureTask 类,代表异步计算的结果。
FutureTask 简介
FutureTask 除了实现 Future 接口外,还实现了 Runnable 接口。因此,FutureTask 可以交给 Executor 执行,也可以由调用线程直接执行(FutureTask.run())根据 FutureTask.run() 方法被执行的时机,FutureTask 可以处于下面三种状态。
- 未启动。run 方法还没有被执行之前,FutureTask 处于未启动状态
- 已启动。run 方法被执行的过程中,FutureTask 处于已启动状态
- 已完成。run 方法执行完成后正常结束,或被取消(FutureTask.cancel(...)),或者执行 run 方法时抛出异常而异常结束,FutureTask 处于已完成状态
当 FutureTask 处于未启动或已启动状态时,执行 FutureTask.get() 方法将导致线程阻塞;当FutureTask 处于已完成状态时,执行 FutureTask.get() 方法将导致调用线程立即返回结果或抛出异常。
当 FutureTask 处于未启动状态时,执行 FutureTask.cancel() 方法将导致此任务永远不会被执行;当处于启动状态时,执行 FutureTask.cancel(true) 方法将以中断执行此任务线程的方式来试图停止任务;执行 FutureTask.cancel(false) 方法将不会对正在执行此任务的线程产生影响;当 FutureTask 处于已完成状态时,执行 FutureTask.cancel(...) 方法将返回 false。
FutureTask 的使用
当一个线程需要等待另一个线程把某个任务执行完成后它才能继续执行,此时可以使用 FutureTask。假设有多个线程执行若干任务,每个任务最多只能被执行一次。当多个线程试图同时执行同一个任务时,只允许一个线程执行任务,其他线程需要等待这个任务执行完成后才能继续执行。
private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<Object, Future<String>>();
private String executionTask(final String taskName) throws ExecutionException, InterruptedException {
while (true) {
Future<String> future = taskCache.get(taskName); //1.1,2.1
if (future == null) {
Callable<String> task = new Callable<String>() {
public String call() throws InterruptedException {
return taskName;
}
};
//1.2创建任务
FutureTask<String> futureTask = new FutureTask<String>(task);
future = taskCache.putIfAbsent(taskName, futureTask); //1.3
if (future == null) {
future = futureTask;
futureTask.run(); //1.4执行任务
}
}
try {
return future.get(); //1.5,2.2线程在此等待任务执行完成
} catch (CancellationException e) {
taskCache.remove(taskName, future);
}
}
}
上述代码的执行示意图如下所示