CompletableFuture 基本用法


一、 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 提供的一部分方法,但它已经足够强大,可以处理大多数异步编程和并发处理的场景。


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

尚品汇-(四)

&#xff08;1&#xff09;商品的基本知识 1.1基本信息—分类 一般情况可以分为两级或者三级。咱们的项目一共分为三级&#xff0c;即一级分类、二级分类、三级分类。 比如&#xff1a;家用电器是一级分类&#xff0c;电视是二级分类&#xff0c;那么超薄电视就是三级分类。…

已解决VirtualMachineError: 虚拟机错误的正确解决方法,亲测有效!!!

已解决VirtualMachineError: 虚拟机错误的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 解决思路 解决方法 分析错误日志 优化代码 内存泄漏排查 优化递归调用 调整JVM参数 使用监控工具 增加物理内存或升级硬件…

Jmeter5.X性能测试【完整版】

目录 一、Http基础协议和解析 1、浏览器的B/S架构和C/S架构 &#xff08;1&#xff09;CS架构 &#xff08;2&#xff09;BS架构 &#xff08;3&#xff09;URL理解 2、Http超文本传输协议 &#xff08;1&#xff09;含义 # 协议 # json协议 # xml协议 &#xff08;…

mysql分析常用锁、动态监控、及优化思考

这里写自定义目录标题 1.未提交事物&#xff0c;阻塞DDL&#xff0c;继而阻塞所有同表的后续操作,查看未提交事务的进程2.存着正在进行的线程数据。3.根据processlist表中的id杀掉未释放的线程4.查看正在使用的表5.mysql为什么state会有waiting for handler commit6.什么情况导…

简易智能家居系统

文章目录 摘要一、系统设计要求及总体设计方案1.1 设计要求1.2 总体设计方案 二、终端结点的设计及实现2.1单片机最小系统2.2 LED灯的控制与工作状态的显示2.2.1 硬件设计2.2.2 软件设计 2.3 温度的测量与显示2.4 火灾的监测与报警2.5 串口的显示与控制 三、网络传输与控制3.1 …

人工智能在影像组学与放射组学中的最新进展|顶刊速递·24-06-22

小罗碎碎念 本期文献速递的主题——人工智能在影像组学中的最新进展。 小罗一直以来的观点&#xff0c;是把大问题分模块拆解——既然我们想做多模态&#xff0c;那么就先了解单模态的研究套路&#xff0c;再去研究不同模态提取的特征如何融合&#xff0c;搞科研的过程也是管理…

Flutter Android 调试桥 (adb)

客户端&#xff1a;用于发送命令。客户端在开发计算机上运行。您可以通过发出 adb 命令从命令行终端调用客户端。 守护程序adbd&#xff1a;用于在设备上运行命令。守护程序在每个设备上作为后台进程运行。 服务器&#xff1a;用于管理客户端与守护程序之间的通信。服务器在开…

刷代码随想录有感(113):动态规划——爬楼梯plus

题干&#xff1a; 代码&#xff1a; #include<bits/stdc.h> using namespace std;int main(){int n,m;cin>>n>>m;vector<int>dp(n 1, 0);dp[0] 1;for(int j 0; j < n; j){for(int i 1; i < m; i){if(j > i)dp[j] dp[j - i];}}cout<&…

【吊打面试官系列-Mysql面试题】什么是存储过程?用什么来调用?

大家好&#xff0c;我是锋哥。今天分享关于 【什么是存储过程&#xff1f;用什么来调用&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是存储过程&#xff1f;用什么来调用&#xff1f; 答&#xff1a;存储过程是一个预编译的 SQL 语句&#xff0c;优点是…

PyScada(三)后端应用

PyScada 的后端应用 使用后端 要使用后端&#xff0c;请在浏览器中打开http://127.0.0.1 &#xff08;将 127.0.0.1 替换为 PyScada 服务器的 IP 或主机名&#xff09;&#xff0c;然后使用安装过程 中定义的管理员帐户登录 &#xff08;TODO 链接到创建超级用户文档&#xf…

【数据分享】《中国法律年鉴》1987-2022

而今天要免费分享的数据就是1987-2022年间出版的《中国法律年鉴》并以多格式提供免费下载。&#xff08;无需分享朋友圈即可获取&#xff09; 数据介绍 自1987年起&#xff0c;《中国法律年鉴》作为一部全面记录中国法律发展进程的重要文献&#xff0c;见证了中国法治建设的每…

G7易流赋能化工物流,实现安全、环保与效率的共赢

近日&#xff0c;中国物流与采购联合会在古都西安举办了备受瞩目的第七届化工物流安全环保发展论坛。以"坚守安全底线&#xff0c;追求绿色发展&#xff0c;智能规划化工物流未来"为主题&#xff0c;该论坛吸引了众多政府部门、行业专家和企业代表的参与。G7易流作为…

unity中使用commandbuffer将自定义画面渲染到主相机上

CommandBuffer 保存渲染命令列表&#xff08;例如设置渲染目标或绘制给定网格&#xff09;。您可以指示 Unity 在内置渲染管线中的各个点安排和执行这些命令&#xff0c;因此&#xff0c;您可以自定义和扩展 Unity 的渲染功能。 这句话意味着你可以通过command buffer让相机渲…

数组和链表的区别是什么?

引言&#xff1a;本文旨在深入探讨数组和链表之间的区别&#xff0c;分析它们在不同情境下的优缺点&#xff0c;并探讨如何根据应用需求选择合适的数据结构。通过深入理解数组和链表的内部工作原理和应用场景&#xff0c;读者将能够更好地应用这些知识解决实际问题&#xff0c;…

web中间件漏洞-Jenkins漏洞-弱口令、反弹shell

web中间件漏洞-Jenkins漏洞-弱口令、反弹shell Jenkins弱口令 默认用户一般为jenkins/jenkins 使用admin/admin123登陆成功 Jenkins反弹shell 格式为 println"命令".execute().text 在/tmp目录中生成shell.sh文件&#xff0c;并向其中写入反弹shell的语句 new…

【金】?Y? python网页前端streamlit

1、如何从 Google Colab Notebook 启动 streamit参考-How to Launch Streamlit App from Google Colab Notebook !streamlit run web.py & npx localtunnel --port 8501 & curl ipv4.icanhazip.com

【Excel经验】字符串处理方法

概览-公式汇总 序号公式功能公式公式示例公式说明1把多列内容拼接在一起&#xff0c;作为新的一列的内容CONCATENATE (text1,text2,…)CONCATENATE(A2,“#”,B2,“”,C2)用于根据多个列的内容拼成我们指定格式的内容&#xff0c;拼接的内容通常来源于原始数据&#xff0c;同时…

keep-alive实现页面缓存

<!--router-view 实现页面缓存--> <router-view v-slot"{Component}"><keep-alive :max"8"><component :is"Component"></component></keep-alive> </router-view>

人工智能导论笔记

有关知识表示和推理的零碎知识点 人工智能导论复习题和概念-CSDN博客 机器学习篇 机器学习分类&#xff08;根据样本数据是否带有标签&#xff09;&#xff1a;监督的机器学习、无监督的机器学习、半监督学习。 监督学习又称为“有教师学习”。在监督学习中&#xff0c;模型…

不到3毛钱的SOT23和SOT89封装18V耐压低功耗高PSRR高精度LDO稳压芯片ME6231电流0.5A电压3.3V和1.8V

前言 SOT23-5封装ME6231外观和丝印 一款国产LDO&#xff0c;某些场合&#xff0c;要把1117扔了吧&#xff0c;SOT23封装&#xff0c;虽然不是最小&#xff0c;但也是够小的了。 参考价格&#xff1a;约0.25元 概述 ME6231 系列是以 CMOS 工艺制造的 18V 耐压、低功耗、高 PSR…