JUC之十一:CompletableFuture用法详解

这篇具有很好参考价值的文章主要介绍了JUC之十一:CompletableFuture用法详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

JUC之十一:CompletableFuture用法详解

一、前言

前面介绍了FutureTask,获取异步执行结果通过get的形式,而且会阻塞主线程,随着开发的越来越越复杂已经无法满足真正开发场景,我们试想一个例子:

我们再炒菜的时候,先洗菜,切菜,炒菜,盛菜,然后吃。。。。。。

如果还用原来的future来操作的话,需要每一个步骤get出前一个线程的结果之后才能继续执行,导致主线程阻塞,无法去干别的事情,如蒸米饭等,为了解决这一问题提出了CompletableFuture的异步编程方式。

FutureTask与CompletableFuture的区别:

1、FutureTask获取异步执行的结果通过get实现,会阻塞主线程 ,CompletableFuture执行完一个阶段之后自动通知下一个阶段开始执行,主线程可继续处理其他的事情。

2、FutureTask没有处理异常的方法,CompletableFuture可处理异步线程内的异常。

3、CompletableFuture可对多个任务进行任意的组合、并行、串行使用。

二、CompletableFuture类简介

继承关系如下所示:

JUC之十一:CompletableFuture用法详解,Java,java,juc

继承自Future接口以及CompletionStage接口,Future前面介绍FutureTask的时候已经介绍过。

CompletionStage接口定义了具体的异步编程的方法,执行某一阶段之后返回一个新的CompletionStage可继续执行下一阶段,CompletableFuture默认使用的线程池是ForkJoinPool.commonPool(),前面介绍过ForkJoinPool使用的是守护线程,所以注意主线程何时结束避免异步执行失败。

private static final Executor asyncPool = useCommonPool ?
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

三、功能分类

3.1、获取结果

  • get:实现自Future接口,获取结果,会阻塞主线程。
  • getTime:实现自Future接口,在指定时间内线程完成获取结果,若未完成则抛出异常。
  • getNow:立即获取结果,若任务完成则返回任务值,否则返回指定的值。
  • join:与get相同作用,区别在于返回的是非检查异常,外部可处理也可不处理,而get必须处理。
  • complete:任务是否完成,若完成返回true,未完成则直接将任务结果设置为指定的值。
        //如果已经完成了返回true,否则返回false,并设置为该值
        boolean complete = future.complete(10);
        System.out.println(complete);
//        获取结果
        Integer get = future.get();
//        获取结果,如果超时直接抛出异常
        Integer getTime = future.get(1, TimeUnit.DAYS);
//        直接获取结果,如果任务没完成,则返回指定的值
        Integer getNow = future.getNow(20);
//        获取结果,与get区别是返回一个(unchecked)CompletionException异常,可处理也可不处理,而get返回检查异常,需要具体的处理
        Integer join = future.join();

3.2、依赖关系

  • thenApply: 拿着上一阶段的执行结果,执行函数,返回结果。
  • thenCompose: 拿着上一阶段的结果,执行新的任务,并返回第二个任务的结果。
  • thenAccept: 消费函数,拿着上一阶段的结果,执行函数。
  • thenRun: 任务执行完直接执行函数,不关心任务结果,无返回值。

3.3、and聚合关系

  • thenCombine: 合并两个任务,两个任务的结果执行函数,返回函数执行后的结果。

  • thenAcceptBoth: 合并两个任务,两个任务的结果执行函数,无返回值。

  • runAfterBoth: 执行完两个任务之后,执行函数,不关心前两个任务的结果。

3.4、or聚合关系

  • applyToEither: 两个线程任务相比较,先获得执行结果的,执行后续的函数返回结果。

  • acceptEither:两个线程任务相比较,先获得执行结果的,执行后续的函数,无返回值。

  • runAfterEither: 两个线程任务任何一个执行完,执行函数,不关心任务结果,无返回值。

3.5、并行执行

  • anyOf: 任何一个线程任务执行结束,返回结果。

  • allOf: 等待所有线程执行结束,无返回值。

3.6、结果处理

  • whenComplete: 任务执行结束,对结果以及异常进行处理,返回同一种类型。

  • exceptionally: 任务执行结束,对异常进行处理。

  • handle: 任务执行结束,对结果以及异常进行处理,返回任意类型的结果。

四、方法详解

CompletionStage接口同一种类型的方法一般是三个

如:

//同步方法
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
//异步方法
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn);
//异步方法,不使用默认的线程池,指定线程池。
public <U> CompletionStage<U> thenApplyAsync(Function<? super T,? extends U> fn,Executor executor);

后面只做方法介绍,不区分同步以及异步。

4.1、结果转换

thenApply:

获取上一任务的执行结果,执行当前函数,返回函数执行结果。

入参:

Function<? super T,? extends U> fn: 函数的入参是前一个任务的结果, 出参是函数的执行结果。

方法:
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);

使用示例:
CompletableFuture<String> thenApply = CompletableFuture
    .supplyAsync(() -> 10)
    .thenApply(x -> "衬衫的价格是:" + x);

thenCompose:

获取上一任务的执行结果,开启新的 CompletableFuture 进行计算。

方法:
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn);

使用实例:
CompletableFuture<String> thenCompose = CompletableFuture
      .supplyAsync(() -> 10)
      .thenCompose(x -> CompletableFuture.supplyAsync(() -> "衬衫的价格是:" + x));

thenApply thenCompose 的区别:

  • thenApply转换的是泛型中的类型,返回的是同一个CompletableFuture
  • thenCompose将内部的CompletableFuture调用展开来并使用上一个CompletableFutre调用的结果在下一步的CompletableFuture调用中进行运算,是生成一个新的CompletableFuture

thenCombine:

组合两个任务,对两个任务结果进行计算,并返回计算结果。

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,
                                              BiFunction<? super T,? super U,? extends V> fn)
 
 使用实例:
        CompletableFuture<String> thenCombine = CompletableFuture
                .supplyAsync(() ->10)
                .thenCombine(CompletableFuture.supplyAsync(() -> 0.8),
                             (r1, r2) -> "衬衫打八折的价格是:" + r1 * r2);

4.2、结果消费

thenAccept:

获取上一任务的执行结果,进行消费,无返回值。

方法:
public CompletableFuture<Void> thenAccept(Consumer<? super T> action);

使用实例:
CompletableFuture<Void> thenAccept = CompletableFuture
      .supplyAsync(() -> 10)
      .thenAccept(x -> System.out.println("衬衫的价格是" + x));

thenRun:

不考虑上一任务的执行结果,执行Runnable函数,无返回值。

方法:
public CompletableFuture<Void> thenRun(Runnable action);

使用实例:
CompletableFuture<Void> thenRun = CompletableFuture
       .supplyAsync(() -> 10)
       .thenRun(() -> System.out.println("执行thenRun"));

thenAcceptBoth:

获取两个任务的执行结果,进行消费。

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
        BiConsumer<? super T, ? super U> action) 
    
使用实例:
CompletableFuture<Void> thenAcceptBoth = CompletableFuture
    .supplyAsync(() -> 10)
    .thenAcceptBoth(CompletableFuture.supplyAsync(() -> 0.8), (r1, r2) -> {
        System.out.println("衬衫打八折的价格是:" + r1 * r2);
    });

runAfterBoth:

执行完两个任务后,执行runnable函数,不关心前两个任务的执行结果,无返回值。

public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action)
    
使用实例:
CompletableFuture<Void> runAfterBoth = CompletableFuture
    .supplyAsync(() -> 10)
    .runAfterBoth(CompletableFuture.supplyAsync(() -> 0.8), () -> {
        System.out.println("runAfterBoth正在执行计算函数");
    });

4.3、任务组合交互

applyToEither:

两个任务谁先执行结束,就做为后续Function函数的计算入参。

public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other,
                                              Function<? super T, U> fn)
    
  使用实例:
        CompletableFuture<Integer> applyToEither = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return 10;
                })
                .applyToEither(CompletableFuture.supplyAsync(() -> {
//                    try {
//                        Thread.sleep(2000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
                    return 20;
                }), r -> r);
    

acceptEither:

两个任务谁先执行结束,就进行后续的消费操作。

public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other,
                                            Consumer<? super T> action)
    
使用实例:
        CompletableFuture<Void> acceptEither = CompletableFuture
                .supplyAsync(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return 10;
                })
                .acceptEither(CompletableFuture.supplyAsync(() -> {
//                    try {
//                        Thread.sleep(2000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
                    return 20;
                }), () -> System.out.println("acceptEither执行结束啦"));

runAfterEither:

两个任务任意一个执行结束,执行Runnable函数。

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,Runnable action)
    
        CompletableFuture<Void> runAfterEither = CompletableFuture
                .supplyAsync(() -> 10)
    		    .runAfterEither(CompletableFuture.supplyAsync(() -> 20), 
                                  () -> System.out.println("runAfterEither执行结束啦"));

anyOf:

任何一个线程任务执行结束,返回结果。

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
    
CompletableFuture<Object> anyOf = CompletableFuture
                .anyOf(CompletableFuture.supplyAsync(() -> 100),
                        CompletableFuture.supplyAsync(() -> "200"));

allOf:

等待所有线程执行结束,无返回值。

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)        

CompletableFuture<Void> allOf = CompletableFuture
                .allOf(CompletableFuture.supplyAsync(() -> 100),
                        CompletableFuture.supplyAsync(() -> "200"));

五、使用案例

实现最优的“烧水泡茶”程序

JUC之十一:CompletableFuture用法详解,Java,java,juc

对于烧水泡茶这个程序,一种最优的分工方案:用两个线程 T1 和 T2 来完成烧水泡茶程序,T1 负责洗水壶、烧开水、泡茶这三道工序,T2 负责洗茶壶、洗茶杯、拿茶叶三道工序,其中 T1 在执行泡茶这道工序时需要等待 T2 完成拿茶叶的工序。

public class CompletableFutureTest {

    public static void main(String[] args) {

        //任务1:洗水壶->烧开水
        CompletableFuture<Void> f1 = CompletableFuture
            .runAsync(() -> {
                System.out.println("T1:洗水壶...");
                sleep(1, TimeUnit.SECONDS);

                System.out.println("T1:烧开水...");
                sleep(15, TimeUnit.SECONDS);
            });
        //任务2:洗茶壶->洗茶杯->拿茶叶
        CompletableFuture<String> f2 = CompletableFuture
            .supplyAsync(() -> {
                System.out.println("T2:洗茶壶...");
                sleep(1, TimeUnit.SECONDS);

                System.out.println("T2:洗茶杯...");
                sleep(2, TimeUnit.SECONDS);

                System.out.println("T2:拿茶叶...");
                sleep(1, TimeUnit.SECONDS);
                return "龙井";
            });
        //任务3:任务1和任务2完成后执行:泡茶
        CompletableFuture<String> f3 = f1.thenCombine(f2, (__, tf) -> {
            System.out.println("T1:拿到茶叶:" + tf);
            System.out.println("T1:泡茶...");
            return "上茶:" + tf;
        });
        //等待任务3执行结果
        System.out.println(f3.join());
    }

    static void sleep(int t, TimeUnit u){
        try {
            u.sleep(t);
        } catch (InterruptedException e) {
        }
    }
}

参考链接:

https://blog.csdn.net/sermonlizhi/article/details/123356877文章来源地址https://www.toymoban.com/news/detail-594827.html

到了这里,关于JUC之十一:CompletableFuture用法详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java的CompletableFuture,Java的多线程开发

    如下图: 以后用到再加 get() 和 join() 方法区别? 都可以阻塞线程 —— 等所有任务都执行完了再执行后续代码。 anyOf() 和 allOf() 的区别? 无返回值 推荐: 开启多线程——无返回值的——阻塞 :test06 有返回值 推荐:开启多线程——有返回值的,返回一个新的List——阻塞—

    2024年02月06日
    浏览(38)
  • 「Java」《深入解析Java多线程编程利器:CompletableFuture》

    多线程编程是指在一个程序中同时执行多个线程来提高系统的并发性和响应性。在现代计算机系统中,多线程编程已经成为开发者日常工作的一部分。以下是对多线程编程需求和挑战的介绍: 需求: 提高系统的性能:通过同时执行多个线程,可以利用多核处理器的优势,实

    2024年02月11日
    浏览(36)
  • Selenium入门用法详解【Java爬虫】

    概述 Selenium 是一个有很多工具和库,可以用来支持浏览器自动化的项目 它能模拟用户与浏览器进行交互,实现 了W3C WebDriver 规范的基础架构 。 Selenium 的核心是WebDriver,可以理解为一个驱动包。 搭建项目 1.先安装Selenium类库(java) 打开你的IDEA,新建一个空的Maven项目, 在项

    2023年04月11日
    浏览(38)
  • Java之SimpleDateFormat的用法详解

    在日常开发中,我们经常会用到时间,我们有很多办法在Java代码中获取时间。但是不同的方法获取到的时间的格式都不尽相同,这时候就需要一种格式化工具,把时间显示成我们需要的格式。 最常用的方法就是使用SimpleDateFormat类。这是一个看上去功能比较简单的类,但是,

    2024年04月13日
    浏览(24)
  • Java中Calendar类用法详解

    Java 中的 Calendar 类是一个抽象类,它提供了一组方法用于操作日期和时间。 以下是 Calendar 类的详细用法: 目录 1. 获取 Calendar 实例: 2. 获取和设置日期和时间: 3. 计算日期和时间: 4. 格式化日期和时间: 要获取 Calendar 实例,可以使用以下 静态方法 之一: Cale

    2024年02月14日
    浏览(52)
  • CompletableFuture:Java中的异步编程利器

    前言: 在秋招的面试中,面试官问了很多关于异步编程相关的知识点,朋友最近也和我聊到了这个话题,因此今天咱们来讨论讨论这个知识点! 随着现代软件系统的日益复杂,对于非阻塞性和响应性的需求也在不断增加。Java为我们提供了多种工具和技术来满足这些需求,其

    2024年02月04日
    浏览(29)
  • Java组合式异步编程CompletableFuture

    CompletableFuture是Java 8中引入的一个功能强大的Future实现类,它的字面翻译是“可完成的Future”。 CompletableFuture对并发编程进行了增强,可以方便地将多个有一定依赖关系的异步任务以流水线的方式组合在一起,大大简化多异步任务的开发。 CompletableFuture实现了两个接口,一个

    2024年04月09日
    浏览(26)
  • Java stream流中peek用法详解

    在Java中,Stream是一种用于处理集合数据的强大工具。它提供了一种函数式编程的方式来对数据进行操作和转换。Stream中的 peek 方法是一种非终端操作,它允许你在流的每个元素上执行一个操作,而不会改变流的内容。 peek 方法的语法如下: 其中, action 是一个接收一个元素并

    2024年02月05日
    浏览(40)
  • Selenium用法详解【键盘控制】【JAVA爬虫】

          本文主要简介如何使用java代码利用Selenium 控制浏览器中需要用到的键盘操作。Selenium 是一种用于自动化浏览器操作和测试的工具,可以通过编程语言与浏览器进行交互。它提供了一组 API 和库,用于模拟用户在浏览器中的各种行为,如点击、输入、导航等。下面将详细

    2024年02月09日
    浏览(39)
  • 【Java8新特性--->异步处理】CompletableFuture

    一、引入 假设一个商品详情页需要以下操作: 查询展示商品的基本信息耗时:0.5s 查询展示商品的销售信息耗时:0.7s 查询展示商品的图片信息耗时:1s 查询展示商品销售属性耗时:0.3s 查询展示商品规格属性耗时:1.5s 查询展示商品详情信息耗时:1s 即使每个查询时间耗时不

    2024年02月06日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包