文章目录

  • 一、Future
    • 1. 什么是Future
    • 2. future如何使用
      • 2.1 FutureTask 工具类
    • 3. Future 接口的局限性
  • 二、CompletableFuture
    • 1. 什么是CompletableFuture
    • 2. 如何使用CompletableFuture
    • 3. demo举例
      • 3.1 以获取股票价格为例,看看如何使用CompletableFuture
      • 3.2 CompletableFuture描述实现串行关系、AND 汇聚关系、 OR汇聚关系
  • 三、参考

一、Future

1. 什么是Future

Java 1.5开始,提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

1、Callable与Runnable
java.lang.Runnable是一个接口,在它里面只声明了一个run()方法,run返回值是void,任务执行完毕后无法返回任何结果

public interface Runnable {public abstract void run();
}

Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法叫做call(),这是一个泛型接口,call()函数返回的类型就是传递进来的V类型

public interface Callable<V> {V call() throws Exception;
}

2、Future + Callable
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果.

public interface Future<V> {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}

2. future如何使用

业务开发中,我们一般首先要创建正确的线程池,那创建完线程池,我们该如何使用呢?

ThreadPoolExecutor 的 void execute(Runnable command) 方法,利用这个方法虽然可以提交任务,但是却没有办法获取任务的执行结果(execute() 方法没有返回值)。我们又都是需要获取任务的执行结果的。那 ThreadPoolExecutor 是否提供了相关功能呢?必须的,这么重要的功能当然需要提供了。

**Java 通过 ThreadPoolExecutor 提供的 3 个 submit() 方法和 1 个 FutureTask 工具类来支持获得任务执行结果的需求。**下面我们先来介绍这 3 个 submit() 方法,这 3 个方法的方法签名如下。


// 提交Runnable任务
Future<?> submit(Runnable task);
// 提交Callable任务
<T> Future<T> submit(Callable<T> task);
// 提交Runnable任务及结果引用
<T> Future<T> submit(Runnable task, T result);

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

  1. 提交 Runnable 任务 submit(Runnable task) :这个方法的参数是一个 Runnable 接口,Runnable 接口的 run() 方法是没有返回值的,所以 submit(Runnable task) 这个方法返回的 Future 仅可以用来断言任务已经结束了,类似于 Thread.join()。
  2. 提交 Callable 任务 submit(Callable task):这个方法的参数是一个 Callable 接口,它只有一个 call() 方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其 get() 方法来获取任务的执行结果。
  3. 提交 Runnable 任务及结果引用 submit(Runnable task, T result):这个方法很有意思,假设这个方法返回的 Future 对象是 f,**f.get() 的返回值就是传给 submit() 方法的参数 result。**这个方法该怎么用呢?下面这段示例代码展示了它的经典用法。需要你注意的是 Runnable 接口的实现类 Task 声明了一个有参构造函数 Task(Result r) ,创建 Task 对象的时候传入了 result 对象,这样就能在类 Task 的 run() 方法中对 result 进行各种操作了。result 相当于主线程和子线程之间的桥梁,通过它主子线程可以共享数据。

2.1 FutureTask 工具类

FutureTask 工具类。前面我们提到的 Future 是一个接口,而 FutureTask 是一个实实在在的工具类,这个工具类有两个构造函数

FutureTask(Callable<V> callable);
FutureTask(Runnable runnable, V result);

那如何使用 FutureTask 呢?其实很简单,**FutureTask 实现了 Runnable 和 Future 接口,由于实现了 Runnable 接口,所以可以将 FutureTask 对象作为任务提交给 ThreadPoolExecutor 去执行,也可以直接被 Thread 执行;又因为实现了 Future 接口,所以也能用来获得任务的执行结果。**下面的示例代码是将 FutureTask 对象提交给 ThreadPoolExecutor 去执行。

// 创建FutureTask
FutureTask<Integer> futureTask  = new FutureTask<>(()-> 1+2);
// 创建线程池
ExecutorService es =   Executors.newCachedThreadPool();
// 提交FutureTask
es.submit(futureTask);
// 获取计算结果
Integer result = futureTask.get();

3. Future 接口的局限性

了解了Future的使用,这里就要谈谈Future的局限性。Future很难直接表述多个Future 结果之间的依赖性,开发中,我们经常需要达成以下目的:

  • 将两个异步计算合并为一个(这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果)
  • 等待 Future 集合中的所有任务都完成。
  • 仅等待 Future 集合中最快结束的任务完成,并返回它的结果。

二、CompletableFuture

1. 什么是CompletableFuture

使用Future获得异步执行结果时,要么调用阻塞方法get(),要么轮询看isDone()是否为true,这两种方法都不是很好,因为主线程也会被迫等待。

Java 在 1.8 版本提供了 CompletableFuture 来支持异步编程。

CompletableFuture类实现了CompletionStage和Future接口,因此你可以像Future那样使用它。它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。

2. 如何使用CompletableFuture

使用CompletableFuture
参考URL: https://www.liaoxuefeng.com/wiki/1252599548343744/1306581182447650

3. demo举例

3.1 以获取股票价格为例,看看如何使用CompletableFuture

public class CompletableFutureDemo {public static void main(String[] args) throws Exception {// 创建异步执行任务:CompletableFuture<Double> cf = CompletableFuture.supplyAsync(CompletableFutureDemo::fetchPrice);// 如果执行成功:cf.thenAccept((result) -> {System.out.println("price: " + result);});// 如果执行异常:cf.exceptionally((e) -> {e.printStackTrace();return null;});// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:Thread.sleep(200);}static Double fetchPrice() {try {Thread.sleep(100);} catch (InterruptedException e) {}if (Math.random() < 0.3) {throw new RuntimeException("fetch price failed!");}return 5 + Math.random() * 20;}}

创建一个CompletableFuture是通过CompletableFuture.supplyAsync()实现的,它需要一个实现了Supplier接口的对象:

public interface Supplier<T> {T get();
}

这里我们用lambda语法简化了一下,直接传入Main::fetchPrice,因为Main.fetchPrice()静态方法的签名符合Supplier接口的定义(除了方法名外)。

紧接着,CompletableFuture已经被提交给默认的线程池执行了,我们需要定义的是CompletableFuture完成时和异常时需要回调的实例。

完成时,CompletableFuture会调用Consumer对象:

可见CompletableFuture的优点是:

  • 异步任务结束时,会自动回调某个对象的方法;
  • 异步任务出错时,会自动回调某个对象的方法;
  • 主线程设置好回调后,不再关心异步任务的执行。

3.2 CompletableFuture描述实现串行关系、AND 汇聚关系、 OR汇聚关系

参考URL: https://time.geekbang.org/column/article/91569

站在分工的角度类比一下工作流。任务是有时序关系的,比如有串行关系、并行关系、汇聚关系等。
CompletableFuture 类还实现了 CompletionStage 接口,CompletionStage 接口可以清晰地描述任务之间的这种时序关系。

  1. 描述串行关系
    CompletionStage 接口里面描述串行关系,主要是 thenApply、thenAccept、thenRun 和 thenCompose 这四个系列的接口。

  2. 描述 AND 汇聚关系
    CompletionStage 接口里面描述 AND 汇聚关系,主要是 thenCombine、thenAcceptBoth 和 runAfterBoth 系列的接口,这些接口的区别也是源自 fn、consumer、action 这三个核心参数不同。

  3. 描述 OR 汇聚关系
    CompletionStage 接口里面描述 OR 汇聚关系,主要是 applyToEither、acceptEither 和 runAfterEither 系列的接口,这些接口的区别也是源自 fn、consumer、action 这三个核心参数不同。

  4. 异常处理
    非异步编程里面,我们可以使用 try{}catch{}来捕获并处理异常,那在异步编程里面,异常该如何处理呢?
    CompletionStage 接口给我们提供的方案非常简单,比 try{}catch{}还要简单,下面是相关的方法,使用这些方法进行异常处理和串行操作是一样的,都支持链式编程方式。

    CompletionStage exceptionally(fn);
    CompletionStage<R> whenComplete(consumer);
    CompletionStage<R> whenCompleteAsync(consumer);
    CompletionStage<R> handle(fn);
    CompletionStage<R> handleAsync(fn);
    

    exceptionally() 的使用非常类似于 try{}catch{}中的 catch{}。

三、参考

Java并发编程系列一:Future和CompletableFuture解析与使用
参考URL: https://www.cnblogs.com/happyliu/archive/2018/08/12/9462703.html

JAVA并发-Future/CompletableFuture相关推荐

  1. Java并发Future

    一.简介 ThreadPoolExecutor线程池获取任务执行结果,用到Future可以实现. 二.获取执行结果 Java通过ThreadPoolExecutor 提供3个submit()方法和1个 ...

  2. [Java并发-14] Future: 优雅的使用多线程

    上一篇,我们详细介绍了如何创建正确的线程池,那创建完线程池,我们该如何使用呢?在上一篇文章中,我们仅仅介绍了 ThreadPoolExecutor 的 void execute(Runnable co ...

  3. Java并发编程Future超详细教程

    非原创,自己保存学习用,传播请参考原文. 原文链接:Java并发编程Future超详细教程 前言 创建线程有几种方式?这个问题的答案应该是可以脱口而出的吧 继承 Thread 类实现 Runnable ...

  4. java并发编程Future类详解

    作用和举例 future类的作用就是为了调用其他线程完成好后的结果,再返回到当前线程中,如上图举例: 小王自己是主线程,叫外卖等于使用future类,叫好外卖后小王就接着干自己的事去了,当外卖到了的时 ...

  5. 15、Java并发编程:Callable、Future和FutureTask

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

  6. Java并发编程 - Executor,Executors,ExecutorService, CompletionServie,Future,Callable

    一.Exectuor框架简介 Java从1.5版本开始,为简化多线程并发编程,引入全新的并发编程包:java.util.concurrent及其并发编程框架(Executor框架). Executor ...

  7. futuretask java 并发请求_Java并发编程:Callable、Future和FutureTask

    Java并发编程:Callable.Future和FutureTask 在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一 ...

  8. futuretask java 并发请求_Java并发机制(9)--Callable、Future、FutureTask的使用

    Java并发编程:Callable.Future.FutureTask的使用 继承关系: 1.接口Callable与Runnable 1.1.Runnable接口中只有一个void run()方法,其 ...

  9. 【Java并发】Runnable、Callable、Future、FutureTask

    创建线程的两种方式 直接继承 Thread 实现 Runnable 接口 这两种方式都有一个缺点:在执行完成任务之后,无法直接获取到最后的执行结果.如果需要获取执行结果,就必须通过共享变量或线程通信的 ...

最新文章

  1. python 流写入文件_python文件流操作
  2. Java核心技术第五章——2.Object类
  3. POJ 2389 Bull Math(大数乘大数)
  4. SAP MM MM17里不能修改物料主数据'Purchasing Value Key'字段值?
  5. “用手机就能访问卫星” 软件定义升级卫星智能
  6. 几个让我印象深刻的面试题(二)
  7. Jupyter Nodebook添加代码提示(Vscode配置Jupyter Notebook运行.ipynb文件)
  8. CRNN:端到端不定长文字识别算法
  9. SQL概述及在网络安全中的应用
  10. package.json mysql_package.json入门
  11. .NET Core 时代已经到了,你准备好了吗
  12. 你会选择深圳还是佛山?
  13. 关注健康,从现在开始(视力篇)
  14. 七年也扶不起的苹果 Siri
  15. 吴恩达老师的机器学习和深度学习课程笔记打印版(全)
  16. java数组整组处理_java – 使它漂亮:同时处理数组
  17. matlab神经网络工具箱
  18. origin数据平滑_origin怎样平滑曲线 看完你就会了
  19. 我的Unity工具类---对象池
  20. c语言如何画函数图形,c语言绘制函数曲线

热门文章

  1. dst发育筛查有意义吗_Dst发育筛查是什么?
  2. matlab实现模糊控制器并仿真,用Matlab实现空调温度模糊控制器的设计与仿真.pdf...
  3. 如何将彩色证件照调成黑白
  4. VBA将ppt保存为html,ppt2013怎样保存为网页的方法
  5. conda:安装python
  6. ssh突然无法登录,报错failed to start openssh daemon
  7. Linux工作站usb口无反应,usb接口没反应怎么办 usb接口没反应解决方法
  8. js UUID 生成
  9. 广义Pareto分布---极值理论的学习3
  10. [计算机组成原理]2-6、算数移位、逻辑移位、循环移位