Bootstrap

Future使用详解

一、概要

Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

在这里插入图片描述

二、方法摘要

在这里插入图片描述

// 取消任务
boolean cancel(boolean mayInterruptIfRunning);

// 获取任务执行结果
V get() throws InterruptedException, ExecutionException;

// 获取任务执行结果,带有超时时间限制
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,  TimeoutException;

// 判断任务是否已经取消
boolean isCancelled();

// 判断任务是否已经结束
boolean isDone();

三、使用实践

3.1TreadPoolExcutor的submit()方法

在这里插入图片描述
这3个submit()方法之间的区别在于方法参数不同。

  • 提交 Runnable 任务 submit(Runnabletask):这个方法的参数是一个Runnable接口,Runnable接口的run()方法是没有返回值的,所以submit(Runnabletask)这个方法返回的Future仅可以用来断言任务已经结束了,类似于 Thread.join()。
  • 提交 Callable 任务 submit(Callable task):这个方法的参数是一个Callable接口,它只有一个call()方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其get()方法来获取任务的执行结果。
  • 提交 Runnable 任务及结果引用submit(Runnable task, Tresult):假设这个方法返回的 Future对象是f,f.get()的返回值就是传给submit()方法的参数result
@Slf4j
public class FutureAndCallableDemo {

   public static void main(String[] args) throws InterruptedException, ExecutionException {
      ExecutorService executorService = Executors.newSingleThreadExecutor();

      // 使用 Callable ,可以获取返回值
      Callable<String> callable = () -> {
         log.info("进入 Callable 的 call 方法");
         // 模拟子线程任务,在此睡眠 5s,
         // 小细节:由于 call 方法会抛出 Exception,这里不用像使用 Runnable 的run 方法那样 try/catch 了
         Thread.sleep(5000);
         return "Hello from Callable";
      };

      log.info("提交 Callable 到线程池");
      Future<String> future = executorService.submit(callable);

      log.info("主线程继续执行");

      log.info("主线程等待获取 Future 结果");
      // Future.get() blocks until the result is available
      String result = future.get();
      log.info("主线程获取到 Future 结果: {}", result);

      executorService.shutdown();
   }
}

submit(Runnable task, T result)的使用实践过了,同时还发现上述三个submit方法返回值都是 Future 接口,Future接口有5个方法,分别是取消任务的方法cancel()、判断任务是否已取消的方法isCancelled()、判断任务是否已结束的方法 isDone()以及2 个获得任务执行结果的 get()get(timeout, unit),其中最后一个 get(timeout, unit) 支持超时机制。通过 Future 接口提交的任务不但能够获取任务执行结果,还可以取消任务。不过需要注意的是:这两个get()方法都是阻塞式的,如果被调用的时候,任务还没有执行完,那么调用 get()方法的线程会阻塞,直到任务执行完才会被唤醒。

while(!future.isDone()) {
   System.out.println("子线程任务还没有结束...");
   Thread.sleep(1000);

   double elapsedTimeInSec = (System.nanoTime() - startTime)/1000000000.0;

 	 // 如果程序运行时间大于 1s,则取消子线程的运行
   if(elapsedTimeInSec > 1) {
      future.cancel(true);
   }
}

四、FutureTask

Future 是一个接口,而FutureTask是一个工具类,它们的参数和前面介绍的submit()方法类似
在这里插入图片描述
在这里插入图片描述

FutureTask 实现了 RunnableFuture 接口,由于实现了 Runnable 接口,所以可以FutureTask 对象作为任务提交给ThreadPoolExecutor 去执行,也可以直接被Thread 执行;又因为实现了 Future 接口,所以也能用来获得任务的执行结果。可以将FutureTask 对象提交给ThreadPoolExecutor 执行。

@Slf4j
public class FutureTaskDemo {

   public static void main(String[] args) throws InterruptedException, ExecutionException {
      // 使用FutureTask
      FutureTask<Integer>futureTask=new FutureTask<>(()->10+22);
     //创建线程池
      ExecutorService executorService = Executors.newSingleThreadExecutor();
      log.info("提交 FutureTask 到线程池");
      Future<String> future = executorService.submit(futureTask);
      log.info("主线程继续执行");
      log.info("主线程等待获取 Future 结果");
      String result = future.get();
      log.info("主线程获取到 Future 结果: {}", result);

      executorService.shutdown();
   }
}

五、FutureTask源码分析

FutureTask源码分析(先引用其他博主的以后补写)

;