java并发编程汇总11:Future、FutureTask

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}


public class FutureTask<V> implements RunnableFuture<V> {
 
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // ...       
    }
 
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        // ...
    }
}

从构造函数可以看到,FutureTask既可以包装Callable任务,也可以包装Runnable任务,但最终都是将Runnable转换成Callable任务,其实是一个适配过程。

通过上面的分析,可以看到,整个Future其实就三个核心组件:

  1. 真实任务/数据类(通常任务执行比较慢,或数据构造需要较长时间);
  2. Future接口(调用方使用该凭证获取真实任务/数据的结果),即Future接口。
  3. Future实现类(用于对真实任务/数据进行包装),即FutureTask实现类。

在J.U.C提供的Future模式中,最重要的就是FutureTask类,FutureTask是在JDK1.5时,随着J.U.C一起引入的,它代表着一个异步任务,这个任务一般提交给Executor执行,当然也可以由调用方直接调用run方法运行。

在《java并发编程的艺术》一书,作者根据FutureTask.run()方法的执行的时机,FutureTask分为了3种状态:

  1. 未启动。FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态。当创建一个FutureTask,还没有执行FutureTask.run()方法之前,FutureTask处于未启动状态。
  2. 已启动。FutureTask.run()方法被执行的过程中,FutureTask处于已启动状态。
  3. 已完成。FutureTask.run()方法执行结束,或者调用FutureTask.cancel(...)方法取消任务,或者在执行任务期间抛出异常,这些情况都称之为FutureTask的已完成状态。

下图总结了FutureTask的状态变化的过程:

由于FutureTask具有这三种状态,因此执行FutureTask的get方法和cancel方法,当前处于不同的状态对应的结果也是大不相同。这里对get方法和cancel方法做个总结:

get方法:

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

cancel方法:

  • 当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将此任务永远不会执行;
  • 当FutureTask处于已启动状态时,执行FutureTask.cancel(true)方法将以中断线程的方式来阻止任务继续进行,如果执行FutureTask.cancel(false)将不会对正在执行任务的线程有任何影响;
  • FutureTask处于已完成状态时,执行FutureTask.cancel(...)方法将返回false

对Future的get()方法和cancel()方法用下图进行总结:

FutureTask除了实现Future接口外,还实现了Runnable接口。

因此,FutureTask可以交给Executor执行,也可以由调用的线程直接执行(FutureTask.run())。

另外,FutureTask的获取也可以通过ExecutorService.submit()方法返回一个FutureTask对象,然后在通过FutureTask.get()或者FutureTask.cancel方法。

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

参考:你听_大神、《java 并发编程的艺术》

猜你喜欢

转载自blog.csdn.net/ScorpC/article/details/113915682