Java8新的异步编程方式CompletableFuture实现

一. Future

JDK 5引入了Future模式。Future接口是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进行异步计算。

Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。

Future的接口很简单,只有五个方法。

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;
}

Future接口的方法介绍如下:

  • boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
  • boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
  • boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
  • V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException
  • V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计 算超时,将抛出TimeoutException

一般情况下,我们会结合Callable和Future一起使用,通过ExecutorService的submit方法执行Callable,并返回Future。

  ExecutorService executor = Executors.newCachedThreadPool();

  Future<String> future = executor.submit(() -> { //Lambda 是一个 callable, 提交后便立即执行,这里返回的是 FutureTask 实例
   System.out.println("running task");
   Thread.sleep(10000);
   return "return task";
  });

  try {
   Thread.sleep(1000);
  } catch (InterruptedException e) {
  }

  System.out.println("do something else"); //前面的的 Callable 在其他线程中运行着,可以做一些其他的事情

  try {
   System.out.println(future.get()); //等待 future 的执行结果,执行完毕之后打印出来
  } catch (InterruptedException e) {
  } catch (ExecutionException e) {

  } finally {
   executor.shutdown();
  }

比起future.get(),其实更推荐使用get (long timeout, TimeUnit unit) 方法,设置了超时时间可以防止程序无限制的等待future的结果。

二. CompletableFuture介绍

2.1 Future模式的缺点

Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。

要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。

2.2 CompletableFuture介绍

Netty、Guava分别扩展了Java 的 Future 接口,方便异步编程。

Java 8新增的CompletableFuture类正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特征,还提供了其它强大的功能,让Java拥有了完整的非阻塞编程模型:Future、Promise 和 Callback(在Java8之前,只有无Callback 的Future)。

CompletableFuture能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。

CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。

三. CompletableFuture特性

3.1 CompletableFuture的静态工厂方法

方法名 描述
runAsync(Runnable runnable) 使用ForkJoinPool.commonPool()作为它的线程池执行异步代码。
runAsync(Runnable runnable, Executor executor) 使用指定的thread pool执行异步代码。
supplyAsync(Supplier<U> supplier) 使用ForkJoinPool.commonPool()作为它的线程池执行异步代码,异步操作有返回值
supplyAsync(Supplier<U> supplier, Executor executor) 使用指定的thread pool执行异步代码,异步操作有返回值

runAsync 和 supplyAsync 方法的区别是runAsync返回的CompletableFuture是没有返回值的。

 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
      System.out.println("Hello");
    });

    try {
      future.get();
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

    System.out.println("CompletableFuture");

而supplyAsync返回的CompletableFuture是由返回值的,下面的代码打印了future的返回值。

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

    try {
      System.out.println(future.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

    System.out.println("CompletableFuture");

3.2 Completable

方法名 描述
complete(T t) 完成异步执行,并返回future的结果
completeExceptionally(Throwable ex) 异步执行不正常的结束

future.get()在等待执行结果时,程序会一直block,如果此时调用complete(T t)会立即执行。

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

    future.complete("World");

    try {
      System.out.println(future.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

执行结果:

World

可以看到future调用complete(T t)会立即执行。但是complete(T t)只能调用一次,后续的重复调用会失效。

如果future已经执行完毕能够返回结果,此时再调用complete(T t)则会无效。

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    future.complete("World");

    try {
      System.out.println(future.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

执行结果:

Hello

如果使用completeExceptionally(Throwable ex)则抛出一个异常,而不是一个成功的结果。

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");

    future.completeExceptionally(new Exception());

    try {
      System.out.println(future.get());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (ExecutionException e) {
      e.printStackTrace();
    }

执行结果:

java.util.concurrent.ExecutionException: java.lang.Exception
...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java8 CompletableFuture详解

    Java 8来了,是时候学一下新的东西了.Java 7和Java 6只不过是稍作修改的版本,而Java 8将会发生重大的改进.或许是Java 8太大了吧?今天我会给你彻底地解释JDK 8中的新的抽象 – CompletableFuture.众所周知,Java 8不到一年就会发布,因此这篇文章是基于JDK 8 build 88 with lambda support的.CompletableFuture extends Future提供了方法,一元操作符和促进异步性以及事件驱动编程模型,它并不止步

  • Java8新的异步编程方式CompletableFuture实现

    一. Future JDK 5引入了Future模式.Future接口是Java多线程Future模式的实现,在java.util.concurrent包中,可以来进行异步计算. Future模式是多线程设计常用的一种设计模式.Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务.期间我自己可以去做任何想做的事情.一段时间之后,我就便可以从Future那儿取出结果. Future的接口很简单,只有五个方法. public interface Future<

  • Java 8 的异步编程利器 CompletableFuture的实例详解

    目录 一个例子回顾Future 一个例子走进CompletableFuture CompletableFuture使用场景 创建异步任务 supplyAsync方法 runAsync方法 任务异步回调 1.thenRun/thenRunAsync 2.thenAccept/thenAcceptAsync 3.thenApply/thenApplyAsync 4.exceptionally 5.whenComplete方法 6.handle方法 多个任务组合处理 AND组合关系 OR组合的关系 区

  • Java8新特性:函数式编程

    首先需要清楚一个概念:函数式接口:它指的是有且只有一个未实现的方法的接口,一般通过FunctionalInterface这个注解来表明某个接口是一个函数式接口.函数式接口是Java支持函数式编程的基础. 1 Java8函数式编程语法入门 Java8中函数式编程语法能够精简代码. 使用Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept,这个方法只有输入而无输出. 现在我们要定义一个Consumer对象,传统的方式是这样定义的: Consumer c = new Consum

  • java8新特性之stream的collect实战教程

    1.list转换成list 不带return方式 List<Long> ids=wrongTmpList.stream().map(c->c.getId()).collect(Collectors.toList()); 带return方式 // spu集合转化成spubo集合//java8的新特性 List<SpuBo> spuBos=spuList.stream().map(spu -> { SpuBo spuBo = new SpuBo(); BeanUtils.c

  • Java并发 CompletableFuture异步编程的实现

    前面我们不止一次提到,用多线程优化性能,其实不过就是将串行操作变成并行操作.如果仔细观察,你还会发现在串行转换成并行的过程中,一定会涉及到异步化,例如下面的示例代码,现在是串行的,为了提升性能,我们得把它们并行化. // 以下两个方法都是耗时操作 doBizA(); doBizB(); //创建两个子线程去执行就可以了,两个操作已经被异步化了. new Thread(()->doBizA()) .start(); new Thread(()->doBizB()) .start(); 异步化,是

  • javascript异步编程的六种方式总结

    异步编程 众所周知 JavaScript 是单线程工作,也就是只有一个脚本执行完成后才能执行下一个脚本,两个脚本不能同时执行,如果某个脚本耗时很长,后面的脚本都必须排队等着,会拖延整个程序的执行.那么如何让程序像人类一样可以多线程工作呢?以下为几种异步编程方式的总结,希望与君共勉. 回调函数 事件监听 发布订阅模式 Promise Generator (ES6) async (ES7) 异步编程传统的解决方案:回调函数和事件监听 初始示例:假设有两个函数, f1 和 f2,f1 是一个需要一定时

  • .NET中的异步编程-EAP/APM使用方法及案例介绍

    从.NET 4.5开始,支持的三种异步编程模式: •基于事件的异步编程设计模式 (EAP,Event-based Asynchronous Pattern) •异步编程模型(APM,Asynchronous Programming Model) •基于任务的编程模型(TAP,Task-based Asynchronous Pattern) 基于任务的异步模式 (TAP) 是基于 System.Threading.Tasks 命名空间的 Task 和 Task<TResult>,用于表示任意异步

  • .NET2.0版本中基于事件的异步编程模式(EAP)

    一.引言 APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题——不支持对异步操作的取消和没有提供对进度报告的功能,对于有界面的应用程序来说,进度报告和取消操作的支持也是必不可少的. 微软在.NET 2.0的时候就为我们提供了一个新的异步编程模型,也就是基于事件的异步编程模型——EAP(Event-based Asynchronous Pattern ). 二.介绍 实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类

  • Java8 使用CompletableFuture 构建异步应用方式

    目录 概述 同步API VS 异步API 同步API 异步API 同步的困扰 实现异步API 将同步方法改为异步方法 处理异常错误 概述 为了展示 CompletableFuture 的强大特性, 创建一个名为 best-price-finder 的应用,它会查询多个在线商店,依据给定的产品或服务找出最低的价格. 这个过程中,会学到几个重要的技能. 如何提供异步API 如何让你使用了同步API的代码变为非阻塞代码 我们将共同学习如何使用流水线将两个接续的异步操作合并为一个异步计算操作. 比如,在

  • java8新特性 stream流的方式遍历集合和数组操作

    前言: 在没有接触java8的时候,我们遍历一个集合都是用循环的方式,从第一条数据遍历到最后一条数据,现在思考一个问题,为什么要使用循环,因为要进行遍历,但是遍历不是唯一的方式,遍历是指每一个元素逐一进行处理(目的),而并不是从第一个到最后一个顺次处理的循环,前者是目的,后者是方式. 所以为了让遍历的方式更加优雅,出现了流(stream)! 1.流的目的在于强掉做什么 假设一个案例:将集合A根据条件1过滤为子集B,然后根据条件2过滤为子集C 在没有引入流之前我们的做法可能为: public cl

随机推荐