CompletableFuture 基本用法

news2025/2/7 13:59:59


一、 CompletableFuture简介

CompletableFuture 是 Java 8 引入的一个功能强大的类,用于异步编程和并发处理。它提供了丰富的 API 来处理异步任务的结果,支持函数式编程风格,并允许通过链式调用组合多个异步操作。

二、CompletableFuture中的方法

1. 创建 CompletableFuture 对象

  • CompletableFuture.supplyAsync(Supplier<U> supplier): 异步执行给定的 Supplier 函数,并返回一个新的 CompletableFuture,当函数完成时,该 CompletableFuture 将以函数的结果完成。

示例代码:

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  
  
public class CompletableFutureExample {  
  
    public static void main(String[] args) throws ExecutionException, InterruptedException {  
        // 使用 supplyAsync 异步执行一个计算  
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  
            // 模拟一个耗时的计算  
            try {  
                Thread.sleep(2000); // 等待 2 秒  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
                throw new IllegalStateException(e);  
            }  
            return "Hello, CompletableFuture!";  
        });  
  
        // 主线程可以继续执行其他任务,而不需要等待上面的计算完成  
  
        // 当需要结果时,可以调用 get() 方法(这会阻塞,直到结果可用)  
        String result = future.get(); // 这里会等待上面的计算完成,然后返回结果  
        System.out.println(result); // 输出 "Hello, CompletableFuture!"  
    }  
}

 这里顺便讲一下get方法:

(1)get方法的作用是等待此 CompletableFuture 完成,然后返回其结果(或抛出异常)。

(2)get方法的返回值是CompletableFuture<T> 里面的泛型的类型;比如上面的例子中CompletableFuture<String> 泛型是String 所以这里future.get()的返回值是String类型

示例1:

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  
  
public class CompletableFutureExample {  
  
    public static void main(String[] args) throws ExecutionException, InterruptedException {  
       
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  
           ...... 
        });  
  
        String result = future.get(); // 因为 CompletableFuture<String> 泛型是String 所以这里future.get()的返回值是String类型
    }  
}

示例2:

import java.util.concurrent.CompletableFuture;  
import java.util.concurrent.ExecutionException;  
  
public class CompletableFutureExample {  
  
    public static void main(String[] args) throws ExecutionException, InterruptedException {  
       
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {  
           ...... 
        });  
  
        Integerresult = future.get(); // 因为 CompletableFuture<Integer> 泛型是Integer所以这里future.get()的返回值是Integer类型
    }  
}

另外,supplyAsync 方法还有一个重载版本,它接受一个 Executor 作为参数,允许你指定用于执行计算的线程池。这对于控制异步任务的执行环境非常有用。例如:

ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池  
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {  
    // ... 耗时的计算 ...  
}, executor); // 使用指定的线程池执行计算

 

  • CompletableFuture.runAsync(Runnable runnable): 异用于异步地执行一个 Runnable 任务,并且不返回任何结果(返回类型为 CompletableFuture<Void>)。这在你只关心任务的执行而不关心其返回值时非常有用。
import java.util.concurrent.CompletableFuture;  
  
public class CompletableFutureExample {  
  
    public static void main(String[] args) {  
        // 使用默认的 ForkJoinPool 异步执行任务  
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {  
            // 模拟一个耗时的任务  
            try {  
                Thread.sleep(2000); // 假设任务需要2秒来完成  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt(); // 恢复中断状态  
                throw new RuntimeException(e); // 抛出运行时异常以便可以看到异常信息  
            }  
            System.out.println("任务执行完毕!");  
        });  
  
        // 在主线程中继续执行其他操作,不需要等待上面的任务完成  
        System.out.println("主线程继续执行...");  
  
        // 如果你想等待任务完成,可以调用 future.join() 或 future.get(),但请注意这可能会阻塞当前线程  
        // 这里我们只是打印出任务是否已经完成  
        System.out.println("任务是否完成: " + future.isDone());  
  
        // 注意:由于任务是异步执行的,所以上面的 isDone() 方法可能返回 false,因为任务可能还没有完成  
  
        // 你可以通过 future.thenRun(...) 来添加在任务完成后要执行的代码  
        future.thenRun(() -> System.out.println("任务完成后执行的代码"));  
  
        // 注意:thenRun 中的代码也是异步执行的,并且可能在主线程之后执行  
  
        // 为了确保主线程在异步任务完成后才结束,可以调用 future.join()  
        try {  
            future.join(); // 等待异步任务完成  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
  
        // 现在可以确定异步任务已经完成  
        System.out.println("主线程结束");  
    }  
}

 

2. 处理异步任务的结果

  • thenApply(Function<? super T,? extends U> fn): 当此 CompletableFuture 完成时,将结果应用于给定的函数,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。
  • thenAccept(Consumer<? super T> action): 当此 CompletableFuture 完成时,对结果执行给定的操作,然后返回 this
  • thenRun(Runnable action): 当此 CompletableFuture 完成时,执行给定的操作,然后返回 this

3. 组合多个 CompletableFuture

  • thenCombine(CompletableFuture<? extends U> other, BiFunction<? super T,? super U,? extends V> fn): 当此 CompletableFuture 和另一个给定的 CompletableFuture 都完成时,使用这两个结果作为参数应用给定的函数,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。

传统写法:

    public static void main(String[] args) {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            // 模拟耗时计算,返回结果
            try {
                Thread.sleep(1000); // 等待1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 42; // 假设这是第一个任务的结果
        });

        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            // 模拟耗时计算,返回结果
            try {
                Thread.sleep(500); // 等待0.5秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 13; // 假设这是第二个任务的结果
        });

// 使用 thenCombine 合并两个任务的结果
        CompletableFuture<Integer> resultFuture = future1.thenCombine(future2, (a, b) -> a + b);


// 等待结果并打印
        Integer join = resultFuture.join();
        System.out.println(join);//55
    }

 链式调用:

   public static void main(String[] args) {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            // 模拟耗时计算,返回结果
            try {
                Thread.sleep(1000); // 等待1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 42; // 假设这是第一个任务的结果
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            // 模拟耗时计算,返回结果
            try {
                Thread.sleep(500); // 等待0.5秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 13; // 假设这是第二个任务的结果
        }),(res1,res2)->{
            int total = res1 + res2;
            return total;
        });

        // 等待结果并打印
        Integer total = future1.join();
        System.out.println(total); // 42+13=55
    }
  • thenCompose(Function<? super T,? extends CompletionStage<U>> fn): 当此 CompletableFuture 完成时,对其结果应用给定的函数,该函数返回一个新的 CompletionStage,然后返回表示该 CompletionStage 结果的 CompletableFuture

 thenCompose是 CompletableFuture 类中的一个方法,它允许你将一个 CompletableFuture 的结果用作另一个 CompletableFuture 计算的输入,从而链式地组合多个异步操作。

使用场景

thenCompose 适用于以下场景:

  1. 连续异步处理:当你需要对一个异步操作的结果进行另一个异步操作时,可以使用 thenCompose 将这两个操作连接在一起。
  2. 避免嵌套:使用 thenCompose 可以避免 Future 的嵌套,使得代码更加简洁和平坦。

传统写法:

package com.etime.test;

import java.util.concurrent.CompletableFuture;

/**
 * @Date 2024/6/22 20:08
 * @Author liukang
 **/
public class SupplyAsyncTest {
    public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            // 模拟耗时计算,返回结果
            try {
                Thread.sleep(1000); // 等待1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 42; // 假设这是计算的结果
        });

        CompletableFuture<Integer> resultFuture = future.thenCompose(value -> {
            // 使用前一个任务的结果(value)作为输入,创建并返回一个新的CompletableFuture
            return CompletableFuture.supplyAsync(() -> value * 2); // 将结果乘以2
        });

// 等待结果并打印
        Integer join = resultFuture.join();
        System.out.println(join);//84
    }


}

 链式调用:

  public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            // 模拟耗时计算,返回结果
            try {
                Thread.sleep(1000); // 等待1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 42; // 假设这是计算的结果
        }).thenCompose(value -> CompletableFuture.supplyAsync(()->{
                    return value*2;
                })
        );

        Integer join = future.join();
        System.out.println(join);

    }
  • allOf(CompletableFuture<?>... cfs): 返回一个新的 CompletableFuture,该 CompletableFuture 在所有给定的 CompletableFuture 都完成时完成。
  • anyOf(CompletableFuture<?>... cfs): 返回一个新的 CompletableFuture,该 CompletableFuture 在任何一个给定的 CompletableFuture 完成时完成。

4. 异常处理

  • exceptionally(Function<Throwable,? extends T> fn): 当此 CompletableFuture 异常完成时,应用给定的函数到异常,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。

exceptionally是Java中CompletableFuture类的一个方法,用于处理异步操作中可能发生的异常。通过调用exceptionally方法并定义一个异常处理函数,你可以确保在异步操作出现异常时能够优雅地处理,并返回一个默认值或其他的值。

    public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            // 模拟耗时计算,返回结果
            try {
                Thread.sleep(1000); // 等待1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 42; // 假设这是计算的结果
        }).thenCompose(value -> CompletableFuture.supplyAsync(()->{
                    return value/0;
                })
        ).exceptionally((e -> {
            System.out.println("发生了异常");
            System.out.println("异常信息为:"+e.getMessage());
            return null;
        }));

        // 等待结果并打印
        Integer join = future.join();
        System.out.println(join);
    }

 

  • handle(BiFunction<? super T,Throwable,? extends U> fn): 当此 CompletableFuture 完成时,无论是正常完成还是异常完成,都将结果和异常(如果有)应用于给定的函数,并返回一个新的 CompletableFuture,该 CompletableFuture 将以函数的结果完成。

5. 其他方法

  • join(): 等待此 CompletableFuture 完成,然后返回其结果(或抛出异常),是get方法的升级版。
  • get(): 等待此 CompletableFuture 完成,然后返回其结果(或抛出异常)。与 join() 类似,但可能抛出 InterruptedException 和 ExecutionException

join()和get()的区别

  1. 抛出异常的方式不同
    • get 方法会抛出 ExecutionException 异常(如果异步任务执行过程中出现异常),这个异常是具体的,需要显式捕获。此外,如果线程在等待过程中被中断,它还会抛出 InterruptedException
    • join 方法则会抛出 CompletionException 异常(如果异步任务执行过程中出现异常),这个异常是 unchecked 的,因此不需要显式捕获。如果线程在等待过程中被中断,它不会抛出 InterruptedException,因为它本身是不可中断的。

  1. 方法调用限制不同
    • get 方法可以在调用时设置等待的超时时间,如果超时还没有获取到结果,就会抛出 TimeoutException 异常。这使得 get 方法在使用时具有更大的灵活性。
    • join 方法则没有这样的超时机制,一旦调用就必须等待任务执行完成才能返回结果。它不能被中断,除非异步任务本身完成。

  1. 返回结果类型不同
    • get 方法返回的是异步任务的执行结果,该结果是泛型类型 T 的,需要强制转换才能获取真正的结果。
    • join 方法同样返回的是异步任务的执行结果,但不需要强制类型转换,因为其结果就是泛型类型 T
  2. 推荐使用方式不同
    • join 方法通常被推荐用于 CompletableFuture,因为它没有受到 interrupt 的干扰,不需要捕获异常,也不需要强制类型转换。这使得代码更加简洁和易于阅读。
    • get 方法则提供了更多的控制选项,如设置超时时间,这在某些需要更细粒度控制的场景下可能是有用的。但需要注意的是,它可能会抛出 InterruptedException,这需要在代码中显式处理。
  3. 阻塞行为
    • 两者都是阻塞方法,都会阻塞当前线程直到异步任务完成。但如上所述,join 方法是不可中断的,而 get 方法可以被中断。

总结来说,join 和 get 方法在 CompletableFuture 中都用于获取异步任务的执行结果,但在抛出异常的方式、方法调用限制、返回结果类型以及推荐使用方式等方面存在显著的区别。根据具体的需求和场景,可以选择使用 join 或 get 方法。

  • complete(T value): 如果尚未完成,则尝试以给定值完成此 CompletableFuture
  • completeExceptionally(Throwable ex): 如果尚未完成,则尝试以给定异常完成此 CompletableFuture

这些只是 CompletableFuture 提供的一部分方法,但它已经足够强大,可以处理大多数异步编程和并发处理的场景。


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

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

相关文章

Rocky Linux archive下载地址

Index of /vault/rocky/https://dl.rockylinux.org/vault/rocky/

【人工智能】—XGBoost算法在构建互联网防火墙异常行为识别模型应用案例

摘要&#xff1a; 近年来&#xff0c;各地党委、政府加快推进新型工业化&#xff0c;部署实施制造强市战略&#xff0c;提出工业企业“智改数转”是推动全市工业经济稳增长的重要引擎&#xff0c;更是稳增长、促发展的重要抓手。今天博主就以互联网防火墙异常行为识别为例给大家…

【Streamlit学习笔记】Streamlit-ECharts箱型图添加均值和最值label

Streamlit-ECharts Streamlit-ECharts是一个Streamlit组件&#xff0c;用于在Python应用程序中展示ECharts图表。ECharts是一个由百度开发的JavaScript数据可视化库Apache ECharts 安装模块库 pip install streamlitpip install streamlit-echarts绘制箱型图展示 在基础箱型…

内容安全复习 7 - 对抗攻击与防御

文章目录 概述攻击对抗性攻击的目的攻击的损失函数如何攻击FGSM黑盒与白盒真实世界的攻击 防御被动防御主动防御 概述 动机 &#xff08;1&#xff09;不仅要在实验室中部署机器学习分类器&#xff0c;也要在现实世界中部署&#xff1b;实际应用 &#xff08;2&#xff09;分类…

从一到无穷大 #29 ByteGraph的计算,内存,存储三级分离方案是否可以通用化为多模数据库

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 引言ByteGraph现有架构阿里云Lindorm腾讯YottaDB多模型化修改点ByteGraph论文中的优化…

IOS开发学习日记(十七)

简单的第三方登录和分享功能 第三方登录系统 URL Scheme&#xff1a;App间的跳转及通信 App间跳转场景 登陆系统&#xff1a; 跨平台&#xff0c;跨App 标记用户&#xff0c;个性化的推送 使用第三方登录&#xff08;减少注册成本 / 无须维护敏感信息&#xff09; 微信 / Q…

068、PyCharm 关于Live Template模板

在 PyCharm 编辑器中&#xff0c;Live Templates 是一种功能强大的工具&#xff0c;可以帮助我们快速插入常用的代码片段或模板。 以下是在 PyCharm 中添加 Live Templates 的步骤&#xff1a; 添加 Live Templates 步骤&#xff1a; 打开 PyCharm 编辑器。 转到菜单栏中的 …

33 - 连续出现的数字(高频 SQL 50 题基础版)

33 - 连续出现的数字 -- 开窗函数lead(col,n) 统计窗口内往下第n行值 -- over(partition by xxx) 按照xxx所有行进行分组 -- over(partition by xxx order by aaa) 按照xxx分组&#xff0c;按照aaa排序select distinct num as ConsecutiveNums from(select num,# 从当前记录获…

Python——常用数据降维算法应用

随着数据的积累&#xff0c;数据的维度越来越高&#xff0c;高维的数据在带来更多信息的同时&#xff0c;也带来了信息冗余、计算困难等问题&#xff0c;所以对数据进行合理的降维&#xff0c;并保留主要信息非常重要。这些问题主要可以通过主成分分析及相关的降维算法来解决。…

国内外大模型生态发展报告!

很多同学只知类似Check GPT或者说对国内的一些比较了解&#xff0c;对国外的不太了解&#xff0c;所以在这总结。 1 大模型的发展 左表 名称参数特点发布时间GPT-215亿英文底模&#xff0c;开源2019年Google T5110亿多任务微调, 开源2019年GPT-3.51750亿人工反馈微调2022年M…

《计算机英语》 Unit 6 Internet 互联网

Section A Internet 互联网 The Internet is a global system of interconnected computer networks that use the standard Internet protocol suite (TCP/IP) to link several billion devices worldwide. 互联网是一个全球性的互连计算机网络系统&#xff0c;使用标准的互联…

Echarts实现动态背景

最近在公司工作中&#xff0c;遇到一个图表的需求&#xff0c;要求实现背景图是可以动态跟随最高点而改变位置的。 好玩的是&#xff0c;我实现了效果&#xff0c;但是没在官方配置项看到我这个用法的出处&#xff0c;所以记录一下。 话不多说&#xff0c;配置项也没什么好解释…

Redis的安装及详解

1.Redis介绍&#xff1f; 1.1 Redis是什么&#xff1f; Redis&#xff08;Remote Dictionary Server,远程字典服务器&#xff09;是一个开源免费的&#xff0c;用C语言编写的一个高性能的分布式内存数据库&#xff0c;基于内存运行并支持持久化的NoSQL数据库。是当前最热门的…

IPv6 address status lifetime

IPv6 地址状态转换 Address lifetime (地址生存期) 每个配置的 IPv6 单播地址都有一个生存期设置&#xff0c;该设置确定该地址在必须刷新或替换之前可以使用多长时间。某些地址设置为“永久”并且不会过期。“首选”和“有效”生存期用于指定其使用期限和可用性。 自动配置的…

开源与在线 M3U8 Downloader 项目介绍及使用指南

M3U8 是一种用于播放列表格式的文件类型&#xff0c;广泛应用于流媒体服务中&#xff0c;特别是 HLS&#xff08;HTTP Live Streaming&#xff09;协议。它包含了一系列的 TS&#xff08;Transport Stream&#xff09;视频片段地址&#xff0c;使得视频能够分段加载&#xff0c…

亿联 AM610 M.2 SSD PCIE 3.0X2 128GB测评

亿联 AM610 M.2 SSD PCIE 3.0X2 128GB测评 厂商&#xff1a;union memory国产固态硬盘SSD。 接口&#xff1a;PCIE 3.0X2 协议&#xff1a;支持NVME 1.协议 固件&#xff1a;固件版本号11.82 读取量&#xff1a;18TB左右 写入量&#xff1a;14TB左右&#xff0c;NAND闪存约被编…

win10/11磁盘管理

win10/11磁盘管理 合并磁盘分区的前提是你的两个磁盘区域是相邻的&#xff0c;比如如下&#xff1a; 如果需要吧这个磁盘进行分解&#xff0c;你可以选择压缩一部分磁盘或者是直接删除卷 我这里的话&#xff0c;因为压缩出来的卷和C盘好像是不相邻的&#xff08;我之前做过&…

kettle从入门到精通 第七十三课 ETL之kettle kettle调用http分页接口教程

场景&#xff1a;kettle调用http接口获取数据&#xff08;由于数据量比较大&#xff0c;鉴于网络和性能考虑&#xff0c;所以接口是个分页接口&#xff09;。 方案&#xff1a;构造页码list&#xff0c;然后循环调用接口。 1、总体设计 1&#xff09;、初始化分页参数pageNum1…

ICP、ISP及IAP烧录介绍

文章目录 不同的程序下载方式一、ICP:In-Circuit Programming二、ISP:In-System Programming三、IAP:In-Application ProgrammingIAP方案设计不同的程序下载方式 目前,单片机的程序烧录方式可以分为三种:ICP、ISP、IAP。 ICP:In Circuit Programing,在电路编程; ISP:…