当前位置: 首页 > 编程笔记 >

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

车辰龙
2023-03-14
本文向大家介绍Java8新的异步编程方式CompletableFuture实现,包括了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
...

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

 类似资料:
  • 主要内容:1.业务问题,2.CompletableFuture介绍,3.创建异步对象,4.计算完成时回调方法,5.线程串行化与并行化方法,6.多任务组合,7.优化商品详情页1.业务问题 询商品详情页的逻辑非常复杂,数据的获取都需要远程调用,必然需要花费更多的时间。 假如商品详情页的每个查询,需要如下标注的时间才能完成 获取sku的基本信息 1.5s 获取sku的图片信息 0.5s 获取spu的所有销售属性 1s sku价格 1.5s 那么,用户需要4.5s后才能看到商品详情页的内容。很显然是不能

  • 主要内容:1.业务问题,2.CompletableFuture介绍,3.创建异步对象,4.计算完成时回调方法,5.线程串行化与并行化方法,6.多任务组合,7.优化商品详情页1.业务问题 询商品详情页的逻辑非常复杂,数据的获取都需要远程调用,必然需要花费更多的时间。 假如商品详情页的每个查询,需要如下标注的时间才能完成 获取sku的基本信息 1.5s 获取sku的图片信息 0.5s 获取spu的所有销售属性 1s sku价格 1.5s 那么,用户需要4.5s后才能看到商品详情页的内容。很显然是不能

  • 本文向大家介绍C#实现异步编程的方法,包括了C#实现异步编程的方法的使用技巧和注意事项,需要的朋友参考一下 最近在我参与的几个.Net项目中都有用到异步编程,作为一名.Net小白,很有必要好好地学习一下C#异步编程。 什么是异步 异步指的就是不用阻塞当前线程来等待任务的完成,而是将任务扔到线程池中去执行,当前线程可以继续向下执行,直至其它线程将任务完成,并回调通知当前线程。整个任务从开始到结束都是

  • 本文向大家介绍JavaScript中实现异步编程模式的4种方法,包括了JavaScript中实现异步编程模式的4种方法的使用技巧和注意事项,需要的朋友参考一下 你可能知道,Javascript语言的执行环境是”单线程“(single thread)。 所谓”单线程”,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。 这种模式的好处是实现起来比

  • 本文向大家介绍简单实现异步编程promise模式,包括了简单实现异步编程promise模式的使用技巧和注意事项,需要的朋友参考一下 异步编程 javascript异步编程, web2.0时代比较热门的编程方式,我们平时码的时候也或多或少用到,最典型的就是异步ajax,发送异步请求,绑定回调函数,请求响应之后调用指定的回调函数,没有阻塞其他代码的执行。还有像setTimeout方法同样也是异步执行回

  • 本文向大家介绍javascript异步编程的六种方式总结,包括了javascript异步编程的六种方式总结的使用技巧和注意事项,需要的朋友参考一下 异步编程 众所周知 JavaScript 是单线程工作,也就是只有一个脚本执行完成后才能执行下一个脚本,两个脚本不能同时执行,如果某个脚本耗时很长,后面的脚本都必须排队等着,会拖延整个程序的执行。那么如何让程序像人类一样可以多线程工作呢?以下为几种异步