futuretask详解 2.使用 3.实现

Future接口和实现Future接口的FutureTask类,代表异步计算的结果。

FutureTask除了实现 Future 接口外,还实现了 Runnable 接口。因此,FutureTask可以交给Executor执行,也可以由调用线程直接执行(FutureTask.run())。根据FutureTask.run()方法被执行的时机,FutureTask可以处于下面3种状态:

  1. 未启动。FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态。当创建一
    个FutureTask,且没有执行FutureTask.run()方法之前,这个FutureTask处于未启动状态。
  2. 已启动。FutureTask.run()方法被执行的过程中,FutureTask处于已启动状态。
  3. 已完成。FutureTask.run()方法执行完后正常结束,或被取消(FutureTask.cancel(…)),或
    执行FutureTask.run()方法时抛出异常而异常结束,FutureTask处于已完成状态。

下图是FutureTask的状态迁移示意图

FutureTask的状态迁移示意图

当FutureTask处于未启动或已启动状态时,执行FutureTask.get()方法将导致调用线程阻塞;当FutureTask处于已完成状态时,执行FutureTask.get()方法将导致调用线程立即返回结果或抛出异常。

当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将导致此任务永远不会被执行;当FutureTask处于已启动状态时,执行FutureTask.cancel(true)方法将以中断执行此任务线程的方式来试图停止任务;当FutureTask处于已启动状态时,执行FutureTask.cancel(false)方法将不会对正在执行此任务的线程产生影响(让正在执行的任务运行完成);当FutureTask处于已完成状态时,执行FutureTask.cancel(…)方法将返回false。

下图是get方法和cancle方法的执行示意图

FutureTask的get和cancel的执行示意图

2.使用

  1. FutureTask交给Executor执行
  2. 通过ExecutorService.submit(…)方法返回一个FutureTask,然后执行FutureTask.get()方法或FutureTask.cancel(…)方法
  3. 单独使用FutureTask。

当一个线程需要等待另一个线程把某个任务执行完后它才能继续执行,此时可以使用
FutureTask。假设有多个线程执行若干任务,每个任务最多只能被执行一次。当多个线程试图
同时执行同一个任务时,只允许一个线程执行任务,其他线程需要等待这个任务执行完后才
能继续执行。下面是对应的示例代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<>();

private String (final String taskName) throws ExecutionException, InterruptedException {
while (true) {
Future<String> future = taskCache.get(taskName);
if (future == null) {
Callable<String> task = new Callable<String>(){

public String call() throws Exception {
return taskName;
}
};
FutureTask<String> futureTask = new FutureTask<>(task);
future = taskCache.putIfAbsent(taskName, futureTask); //1.3
if (future == null) {
future = futureTask;
futureTask.run(); //1.4执行任务
}
}
try {
return future.get(); // 1.5获取结果
} catch (CancellationException e) {
taskCache.remove(taskName, future);
}
}
}

代码的执行示意图

当两个线程试图同时执行同一个任务时,如果Thread 1执行1.3后Thread 2执行2.1,那么接
下来Thread 2将在2.2等待,直到Thread 1执行完1.4后Thread 2才能从2.2(FutureTask.get())返回。

3.实现