CompletableFuture详解

news2024/9/27 17:25:11

CompletableFuture详解

概要

  • Runnable+Thread虽然提供了多线程的能力但是没有返回值
  • Callable+Thread的方法提供多线程和返回值的能力但是在获取返回值的时候会阻塞主线程

上述的情况只适合不关心返回值,只要提交的Task执行了就可以。另外的就是能够容忍等待。

CompletableFuture是jdk8的新特性。CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步会点、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。

① 创建异步任务

1、supplyAsync

supplyAsync是创建带有返回值的异步任务。它有如下两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重

载方法

// 带返回值异步请求,默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
 
// 带返回值的异步请求,可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

2、runAsync

runAsync是创建没有返回值的异步任务。它有如下两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重载

方法

// 不带返回值的异步请求,默认线程池
public static CompletableFuture<Void> runAsync(Runnable runnable)
 
// 不带返回值的异步请求,可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

3、获取任务结果

// 如果完成则返回结果,否则就抛出具体的异常
public T get() throws InterruptedException, ExecutionException 
 
// 最大时间等待返回结果,否则就抛出具体异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
 
// 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T join()
 
// 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
public T getNow(T valueIfAbsent)
 
// 如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
 
// 如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex) 

② 异步回调处理

1、结果转换

将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果。

thenApply

thenApply接收一个函数作为参数,使用该函数处理上一个CompletableFuture调用的结果,并返回一个具有处理结果的Future对象。

常用方法:

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)

具体使用:

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    int result = 100;
    System.out.println("第一次运算:" + result);
    return result;
}).thenApply(number -> {
    int result = number * 3;
    System.out.println("第二次运算:" + result);
    return result;
});
thenCompose

thenCompose的参数为一个返回CompletableFuture实例的函数,该函数的参数是先前计算步骤的结果。

常用方法:

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

具体使用:

CompletableFuture<Integer> future = CompletableFuture
    .supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            int number = new Random().nextInt(30);
            System.out.println("第一次运算:" + number);
            return number;
        }
    })
    .thenCompose(new Function<Integer, CompletionStage<Integer>>() {
        @Override
        public CompletionStage<Integer> apply(Integer param) {
            return CompletableFuture.supplyAsync(new Supplier<Integer>() {
                @Override
                public Integer get() {
                    int number = param * 2;
                    System.out.println("第二次运算:" + number);
                    return number;
                }
            });
        }
    });

thenApply 和 thenCompose的区别

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

2、结果消费

结果消费系列函数只对结果执行Action,而不返回新的计算值。

根据对结果的处理方式,结果消费函数又可以分为下面三大类:

  • thenAccept():对单个结果进行消费
  • thenAcceptBoth():对两个结果进行消费
  • thenRun():不关心结果,只对结果执行Action
thenAccept

常用方法:

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);

具体使用:

CompletableFuture<Void> future = CompletableFuture
    .supplyAsync(() -> {
        int number = new Random().nextInt(10);
        System.out.println("第一次运算:" + number);
        return number;
    }).thenAccept(number ->
                  System.out.println("第二次运算:" + number * 5));
thenAcceptBoth

thenAcceptBoth函数的作用是,当两个CompletionStage都正常完成计算的时候,就会执行提供的action消费两个异步的结果。

常用方法:

public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);

具体使用:

CompletableFuture<Integer> futrue1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(3) + 1;
        try {
            TimeUnit.SECONDS.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务1结果:" + number);
        return number;
    }
});

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(3) + 1;
        try {
            TimeUnit.SECONDS.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务2结果:" + number);
        return number;
    }
});

futrue1.thenAcceptBoth(future2, new BiConsumer<Integer, Integer>() {
    @Override
    public void accept(Integer x, Integer y) {
        System.out.println("最终结果:" + (x + y));
    }
});
thenRun

thenRun也是对线程任务结果的一种消费函数,与thenAccept不同的是,thenRun会在上一阶段 CompletableFuture计算完成的时候执行一个Runnable,而Runnable并不使用该CompletableFuture计算的结果。

常用方法:

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);

具体使用:

CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
    int number = new Random().nextInt(10);
    System.out.println("第一阶段:" + number);
    return number;
}).thenRun(() ->
           System.out.println("thenRun 执行"));

3、结果组合

thenCombine

合并两个线程任务的结果,并进一步处理。

常用方法:

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);

public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);

public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor);

具体使用:

CompletableFuture<Integer> future1 = CompletableFuture
    .supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            int number = new Random().nextInt(10);
            System.out.println("任务1结果:" + number);
            return number;
        }
    });
CompletableFuture<Integer> future2 = CompletableFuture
    .supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            int number = new Random().nextInt(10);
            System.out.println("任务2结果:" + number);
            return number;
        }
    });
CompletableFuture<Integer> result = future1
    .thenCombine(future2, new BiFunction<Integer, Integer, Integer>() {
        @Override
        public Integer apply(Integer x, Integer y) {
            return x + y;
        }
    });
System.out.println("组合后结果:" + result.get());

4、结果交互

线程交互指将两个线程任务获取结果的速度相比较,按一定的规则进行下一步处理

applyToEither

两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作。

常用方法:

public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);

具体使用:

CompletableFuture<Integer> future1 = CompletableFuture
    .supplyAsync(new Supplier<Integer>() {
        @Override
        public Integer get() {
            int number = new Random().nextInt(10);
            try {
                TimeUnit.SECONDS.sleep(number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务1结果:" + number);
            return number;
        }
    });
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(10);
        try {
            TimeUnit.SECONDS.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务2结果:" + number);
        return number;
    }
});

future1.applyToEither(future2, new Function<Integer, Integer>() {
    @Override
    public Integer apply(Integer number) {
        System.out.println("最快结果:" + number);
        return number * 2;
    }
});
acceptEither

两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作。

常用方法:

public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);

具体使用:

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(10) + 1;
        try {
            TimeUnit.SECONDS.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("第一阶段:" + number);
        return number;
    }
});

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(10) + 1;
        try {
            TimeUnit.SECONDS.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("第二阶段:" + number);
        return number;
    }
});

future1.acceptEither(future2, new Consumer<Integer>() {
    @Override
    public void accept(Integer number) {
        System.out.println("最快结果:" + number);
    }
});
runAfterEither

两个线程任务相比较,有任何一个执行完成,就进行下一步操作,不关心运行结果。

常用方法:

public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);

具体使用:

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(5);
        try {
            TimeUnit.SECONDS.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务1结果:" + number);
        return number;
    }
});

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(new Supplier<Integer>() {
    @Override
    public Integer get() {
        int number = new Random().nextInt(5);
        try {
            TimeUnit.SECONDS.sleep(number);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务2结果:" + number);
        return number;
    }
});

future1.runAfterEither(future2, new Runnable() {
    @Override
    public void run() {
        System.out.println("已经有一个任务完成了");
    }
}).join();
anyOf

anyOf() 的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回这个 CompletableFuture

常用方法:

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)

具体使用:

Random random = new Random();
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(random.nextInt(5));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "hello";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(random.nextInt(1));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "world";
});
CompletableFuture<Object> result = CompletableFuture.anyOf(future1, future2);
allOf

allOf方法用来实现多 CompletableFuture 的同时返回。

常用方法:

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

具体使用:

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("future1完成!");
    return "future1完成!";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    System.out.println("future2完成!");
    return "future2完成!";
});

CompletableFuture<Void> combindFuture = CompletableFuture.allOf(future1, future2);

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

汇总

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150819.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java并发容器

一、并发容器总结1、大部分在 java.util.concurrent 包中。ConcurrentHashMap: 线程安全的HashMapCopyOnWriteArrayList: 线程安全的List&#xff0c;在读多写少的场合性能非常好&#xff0c;远远好于Vector.ConcurrentLinkedQueue: 高效的并发队列&#xff0c;使用链表实现。可…

[ 数据结构 ] 平衡二叉树(AVL)--------左旋、右旋、双旋

0 引出 数列{1,2,3,4,5,6}&#xff0c;要求创建一颗二叉排序树(BST), 并分析问题所在 回顾:二叉搜索树 左子树全部为空&#xff0c;从形式上看&#xff0c;更像一个单链表.插入速度没有影响查询速度明显降低(因为需要依次比较), 不能发挥 BST的优势&#xff0c;因为每次还需要…

第四十二章 动态规划——数字三角形模型

第四十二章 动态规划——数字三角形模型一、数字三角形模型1、什么是数字三角形模型二、例题1、AcWing 1015. 摘花生&#xff08;1&#xff09;问题&#xff08;2&#xff09;思路状态表示状态转移循环设计初末状态&#xff08;3&#xff09;代码2、AcWing 1018. 最低通行费&am…

第二章:感知机

文章目录2.1感知机模型2.2感知机策略2.3梯度下降法2.4感知机-原始算法形式2.5感知机-原始算法实例2.6感知机-对偶形式2.7感知机-对偶形式实例2.8感知机算法收敛性证明如下&#xff1a;2.1感知机模型 2.2感知机策略 损失函数非负&#xff0c;策略是选择使其最小的模型参数 2.3梯…

【Linux工具】-gcc/g++

gcc/g一&#xff0c;简介二&#xff0c;代码的翻译过程1&#xff0c;预处理2&#xff0c;编译3&#xff0c;汇编4&#xff0c;链接&#xff08;1&#xff09;静态库&#xff08;2&#xff09;动态库&#xff08;3&#xff09;动静态库比较三&#xff0c;常见选项一&#xff0c;…

模板(C++)

模板&#xff08;C&#xff09;泛型编程函数模板概念格式原理实例化匹配原则类模板定义格式实例化泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int& left, int& right) {int tmp left;left right;right tmp; }void Swap(double& left, doub…

【Spring6源码・IOC】BeanDefinition的加载

哎呀&#xff0c;又是午夜时分&#xff0c;又是一个失眠的夜晚&#xff0c;和去年一样&#xff0c;记得去年今日&#xff0c;也是睡不着觉&#xff0c;就注册了csdn的账号&#xff0c;开始写东西&#xff0c;csdn真是深夜最好的安魂剂。 Spring都发布了6.0&#xff0c;这不赶紧…

2022.12青少年软件编程(Python)等级考试试卷(三级)

2022.12.10青少年软件编程(Python)等级考试试卷(三级) 一、单选题(共25题,共50分) 1.列表L1中全是整数,小明想将其中所有奇数都增加1,偶数不变,于是编写了如下图所示的代码。 请问,图中红线处,代码应该是?(D) A. x || 2 B. x ^ 2 C. x && 2 D. x %…

JS日期与字符串相互转换(时间格式化YYYY-MM-DD,Dayjs的使用)

JS日期与字符串相互转换——JS封装函数&#xff0c;Dayjs转换时间格式相关文章调用场景复现一、JS封装函数1、日期转字符串2、字符串转日期二、 Dayjs转换时间格式1、Dayjs快速安装与使用2、Dayjs格式化日期相关文章调用 文章内容文章链接JS数组对象——根据日期进行排序&…

南邮数据结构考研常用算法

1.链表 在带头结点的链表中&#xff0c;删除所有值为x的结点 void Del_X(Linklist &L,ElemType x){LNode *pL->next, *preL,*q;while (p!null){if (p->datax){qp;pp->next;pre->nextp;free(q);}else{prep;pp->next;}} }使用单链表进行插入排序 ListNode*…

数组常用方法总结 (1) :pop / push / shift / unshift

pop 从尾部删除一项。改变原有数组。返回删除掉的内容。 <template><div class"myBlock"><div class"tableBlock"><div class"title">{{ newObject ? "操作后的数组" : "操作前的数组" }}</d…

从0到1完成一个Vue后台管理项目(十、列表API封装、Table列表渲染、表格数据转换)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; 从0到1完成一个Vu…

docker安装及安装过程中遇到的问题

安装Docker-CE 备注&#xff1a;Docker 安装&#xff0c;请参考 Docker 官⽅⽂档: Install Docker Engine on Ubuntu | Docker Documentation 也可参照如下命令进行快速安装。 Ubuntu 卸载旧版本&#xff08;视需要&#xff09; $ sudo apt-get remove docker docker-engin…

Java并发(4)- synchronized与CAS

引言 上一篇文章中我们说过&#xff0c;volatile通过lock指令保证了可见性、有序性以及“部分”原子性。但在大部分并发问题中&#xff0c;都需要保证操作的原子性&#xff0c;volatile并不具有该功能&#xff0c;这时就需要通过其他手段来达到线程安全的目的&#xff0c;在Ja…

因子图--isam相关内容总结

编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;平方根法--通过平方根的方法&#xff0c;发现矩阵新增加的变量都会出现在最后一行。Givens旋转方法求解Ax…

Linux环境下,java调用C/C++动态库

目录 一、环境准备 1、安装gcc/g 2、下载jdk库并配置运行环境 二、配合Java程序创建C程序的动态库 1、生成要求清单 2、交给C 去实现 (1) 接口函数实现 (2) 创建动态库 (3) 检查动态库是否正常链接 3、测试&#xff1a;Java程序调用C动态库 一、环境准备 既然是同时…

百万级数据以Excel形式导出

(1).主要考虑到两个方面&#xff0c;第一个方面是内存溢出问题&#xff0c;所以选用阿里的EasyExcel因为它对POI进行了封装功能强大&#xff1b;第二个方面是由于excel版本导致Sheet存储容量不一样&#xff0c;cexcel2003(.xls)每个Sheet只能存6万多条数据而cexcel2007(xlsx)能…

【自学Python】Python布尔型(bool)

Python布尔型(bool) Python布尔型(bool)教程 Python 布尔类型也叫 bool 类型&#xff0c;Python 布尔类型取值为 True 和 False。Python bool 类型的 True 表示条件为真&#xff0c; False 表示条件为假。 Python布尔型(bool) Python 中的布尔类型可以当做 整数 来对待&…

LeetCode 287. 寻找重复数难度中等2004

&#x1f308;&#x1f308;&#x1f604;&#x1f604;欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 287. 寻找重复数难度中等2004&#xff0c;做好准备了么&#xff0c;那么开始吧。&#x1f332;&#x1f332;&#x1f434;&#x1f434;一、题目名称LeetCo…

怎么在Gitee(码云)上传一个项目(一分钟)

目录怎么在Gitee&#xff08;码云&#xff09;上传一个项目1、工具1.1、Git1.2、新建仓库2、上传流程3、回答上传项目流程中的几个疑问&#xff1f;怎么在Gitee&#xff08;码云&#xff09;上传一个项目 1、工具 1.1、Git 在Git官网或者利用镜像下载符合自己电脑操作系统版…