Java异步编程:CompletionStage接口详解

news2025/6/4 4:03:47

CompletionStage 接口分析

接口能力概述

CompletionStage 是 Java 8 引入的接口,用于表示异步计算的一个阶段,它提供了强大的异步编程能力:

  1. ​链式异步操作​​:允许将一个异步操作的结果传递给下一个操作
  2. ​组合操作​​:可以组合多个 CompletionStage
  3. ​异常处理​​:提供对异步计算中异常的处理机制
  4. ​多种执行方式​​:支持同步、默认异步和自定义执行器的异步执行

主要功能分类

1. 单阶段依赖操作

  • thenApply() - 转换结果
  • thenAccept() - 消费结果
  • thenRun() - 执行无结果操作

2. 双阶段组合操作

  • thenCombine() - 两个阶段都完成后合并结果
  • thenAcceptBoth() - 两个阶段都完成后消费结果
  • runAfterBoth() - 两个阶段都完成后执行操作

3. 任一阶段完成操作

  • applyToEither() - 任一阶段完成后转换结果
  • acceptEither() - 任一阶段完成后消费结果
  • runAfterEither() - 任一阶段完成后执行操作

4. 异常处理

  • exceptionally() - 异常时提供替代值
  • handle() - 无论成功或异常都处理
  • whenComplete() - 无论成功或异常都执行操作

5. 组合其他 CompletionStage

  • thenCompose() - 扁平化嵌套的 CompletionStage

默认方法

从 Java 12 开始,CompletionStage 接口新增了一些默认方法,主要用于更灵活的异常处理:

  1. exceptionallyAsync(Function<Throwable, ? extends T> fn)

    • 异步处理异常的默认方法
  2. exceptionallyAsync(Function<Throwable, ? extends T> fn, Executor executor)

    • 使用指定执行器异步处理异常的默认方法
  3. exceptionallyCompose(Function<Throwable, ? extends CompletionStage<T>> fn)

    • 异常时返回新的 CompletionStage 的默认方法
  4. exceptionallyComposeAsync(Function<Throwable, ? extends CompletionStage<T>> fn)

    • 异步方式异常时返回新的 CompletionStage 的默认方法
  5. exceptionallyComposeAsync(Function<Throwable, ? extends CompletionStage<T>> fn, Executor executor)

    • 使用指定执行器异步方式异常时返回新的 CompletionStage 的默认方法

执行模式

大多数方法都有三种变体:

  1. ​同步​​:基本方法(如 thenApply
  2. ​默认异步​​:带 Async 后缀(如 thenApplyAsync
  3. ​自定义执行器异步​​:带 Async 后缀和 Executor 参数(如 thenApplyAsync(fn, executor)

总结

CompletionStage 接口为 Java 异步编程提供了强大的构建块,允许开发者以声明式的方式组合异步操作,处理成功和失败情况,并控制操作的执行方式(同步或异步)。从 Java 12 开始,通过新增的默认方法进一步增强了异常处理的灵活性。

CompletionStage 默认方法实现分析

CompletionStage 接口从 Java 12 开始引入了一系列默认方法,主要增强了异常处理的灵活性。这些默认方法提供了开箱即用的实现,但它们的正确运行依赖于接口中其他基本方法的正确实现。

默认方法分类与实现分析

1. 异步异常处理 (exceptionallyAsync)

public default CompletionStage<T> exceptionallyAsync(Function<Throwable, ? extends T> fn) {
    return handle((r, ex) -> (ex == null)
                  ? this
                  : this.<T>handleAsync((r1, ex1) -> fn.apply(ex1)))
        .thenCompose(Function.identity());
}

​实现分析​​:

  1. 首先调用 handle 方法检查是否有异常
  2. 如果没有异常(ex == null),返回当前阶段本身
  3. 如果有异常,使用 handleAsync 异步执行异常处理函数
  4. 最后通过 thenCompose 扁平化结果

​依赖的子类实现​​:

  • handle()
  • handleAsync()
  • thenCompose()

2. 带执行器的异步异常处理 (exceptionallyAsync with Executor)

public default CompletionStage<T> exceptionallyAsync(Function<Throwable, ? extends T> fn, Executor executor) {
    return handle((r, ex) -> (ex == null)
                  ? this
                  : this.<T>handleAsync((r1, ex1) -> fn.apply(ex1), executor))
        .thenCompose(Function.identity());
}

​实现分析​​:
与上一个方法类似,但使用指定的 Executor 来执行异步操作

​依赖的子类实现​​:

  • handle()
  • handleAsync(Executor)
  • thenCompose()

3. 异常组合处理 (exceptionallyCompose)

public default CompletionStage<T> exceptionallyCompose(Function<Throwable, ? extends CompletionStage<T>> fn) {
    return handle((r, ex) -> (ex == null)
                  ? this
                  : fn.apply(ex))
        .thenCompose(Function.identity());
}

​实现分析​​:

  1. 检查是否有异常
  2. 无异常时返回当前阶段
  3. 有异常时调用函数生成新的 CompletionStage
  4. 使用 thenCompose 扁平化结果

​依赖的子类实现​​:

  • handle()
  • thenCompose()

4. 异步异常组合处理 (exceptionallyComposeAsync)

public default CompletionStage<T> exceptionallyComposeAsync(Function<Throwable, ? extends CompletionStage<T>> fn) {
    return handle((r, ex) -> (ex == null)
                  ? this
                  : this.handleAsync((r1, ex1) -> fn.apply(ex1))
                    .thenCompose(Function.identity()))
        .thenCompose(Function.identity());
}

​实现分析​​:
类似 exceptionallyCompose,但使用异步方式处理异常

​依赖的子类实现​​:

  • handle()
  • handleAsync()
  • thenCompose()

子类需要提供的内容

虽然这些是默认方法,但它们的正确运行依赖于接口中其他基本方法的正确实现。子类需要确保以下方法的正确实现:

  1. ​基本处理方法​​:

    • handle()
    • handleAsync()
    • handleAsync(Executor)
  2. ​组合方法​​:

    • thenCompose()
    • thenComposeAsync()
    • thenComposeAsync(Executor)
  3. ​其他基础方法​​:

    • 所有非默认的 CompletionStage 方法,因为默认方法构建在这些基础方法之上

实现注意事项

  1. ​线程安全​​:子类实现必须保证线程安全,因为 CompletionStage 可能被多个线程访问

  2. ​执行保证​​:子类必须确保异步方法(...Async)确实在另一个线程执行

  3. ​异常传播​​:必须正确实现异常传播机制,确保异常能通过依赖链传递

  4. ​完成状态​​:必须正确维护阶段的完成状态(正常完成/异常完成)

  5. ​执行顺序​​:必须保证依赖操作的执行顺序符合接口规范

示例:自定义实现的关键点

如果创建自定义的 CompletionStage 实现,必须特别注意:

class MyCompletionStage<T> implements CompletionStage<T> {
    // 必须实现所有非默认方法
    @Override
    public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn) {
        // 实现转换逻辑
    }
    
    @Override
    public <U> CompletionStage<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) {
        // 实现处理逻辑
    }
    
    // 其他必要方法的实现...
    
    // 默认方法会自动继承,但依赖于上述方法的正确实现
}

CompletionStage 默认方法的使用场景

CompletionStage 接口的默认方法(特别是 Java 12 引入的异常处理方法)在以下场景中特别有用:

1. 异步异常处理场景

​典型场景​​:当需要异步处理异常且不希望阻塞当前线程时

CompletionStage<String> stage = someAsyncOperation()
    .exceptionallyAsync(ex -> {
        // 异步执行异常恢复逻辑
        log.error("Operation failed, using fallback", ex);
        return "fallback-value";
    });

​优势​​:

  • 异常处理不会阻塞调用线程
  • 适合处理耗时的异常恢复逻辑(如远程调用备用服务)

2. 需要自定义线程池的异常处理

​典型场景​​:当异常处理需要特定线程池资源时

ExecutorService recoveryExecutor = Executors.newFixedThreadPool(2);

CompletionStage<String> stage = someAsyncOperation()
    .exceptionallyAsync(ex -> {
        // 在专用线程池中执行恢复逻辑
        return callBackendServiceB();
    }, recoveryExecutor);

​优势​​:

  • 避免异常处理占用主业务线程池
  • 可以为不同类型的恢复操作分配不同的线程资源

3. 需要返回新 CompletionStage 的异常恢复

​典型场景​​:当异常发生时需要触发另一个异步操作来恢复

CompletionStage<String> stage = someAsyncOperation()
    .exceptionallyCompose(ex -> {
        // 当主操作失败时,尝试备用方案
        return fallbackAsyncOperation();
    });

​实际应用​​:

  • 主数据库查询失败时尝试从缓存获取
  • 主服务不可用时调用备用服务

4. 复杂的异常处理流水线

​典型场景​​:需要多级异常恢复策略时

CompletionStage<String> stage = someAsyncOperation()
    .exceptionallyComposeAsync(ex -> {
        // 第一级恢复:尝试本地备用方案
        return tryLocalRecovery();
    })
    .exceptionallyComposeAsync(ex -> {
        // 第二级恢复:尝试远程恢复
        return tryRemoteRecovery();
    }, remoteRecoveryExecutor)
    .exceptionally(ex -> {
        // 最后兜底方案
        return "ultimate-fallback";
    });

​优势​​:

  • 构建多层次的弹性恢复策略
  • 每级恢复可以使用不同的执行策略(同步/异步/特定线程池)

5. 与现有代码的集成

​典型场景​​:当需要将异常处理函数封装为 CompletionStage 时

Function<Throwable, String> legacyRecovery = ex -> {
    // 传统的同步恢复逻辑
    return LegacyRecoveryService.recover(ex);
};

// 将传统恢复逻辑适配为异步处理
CompletionStage<String> stage = someAsyncOperation()
    .exceptionallyAsync(legacyRecovery);

​优势​​:

  • 将同步恢复逻辑自动提升为异步处理
  • 无需修改原有恢复逻辑的实现

6. 需要保留堆栈信息的场景

​典型场景​​:当异步操作链中需要保留原始异常信息时

CompletionStage<String> stage = someAsyncOperation()
    .handleAsync((result, ex) -> {
        if (ex != null) {
            // 在此添加额外上下文信息
            throw new RecoveryException("Failed in async operation", ex);
        }
        return result;
    })
    .exceptionallyComposeAsync(ex -> {
        // 可以访问到包装后的异常信息
        RecoveryException re = (RecoveryException) ex.getCause();
        return recoveryWithContext(re.getContext());
    });

​优势​​:

  • 避免异步操作链中异常信息丢失
  • 可以在不同阶段添加诊断上下文

何时选择默认方法 vs 基础方法

场景使用默认方法使用基础方法
简单的同步异常处理-exceptionally()
异步异常处理exceptionallyAsync()-
需要控制异常处理线程exceptionallyAsync(fn, executor)-
异常时需要触发新异步操作exceptionallyCompose()-
简单的异常转换-handle()
需要同时处理成功和失败-whenComplete()

实际案例:服务降级策略

public CompletionStage<Response> handleRequest(Request request) {
    return primaryService.callAsync(request)
        .exceptionallyComposeAsync(ex -> {
            if (ex instanceof TimeoutException) {
                // 超时快速失败,不尝试降级
                throw new ServiceException("Timeout", ex);
            }
            // 其他异常尝试降级
            return fallbackService.callAsync(request);
        }, fallbackExecutor)
        .exceptionally(ex -> {
            // 记录最终失败
            metrics.recordFailure(ex);
            return Response.failure("Service unavailable");
        });
}

在这个案例中,我们:

  1. 首先尝试主服务调用
  2. 如果失败(非超时),异步尝试降级服务
  3. 使用专门的线程池执行降级逻辑
  4. 最后兜底记录指标并返回友好错误

这些默认方法让复杂的异步错误处理模式能够以声明式的方式简洁表达。

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

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

相关文章

基于微信小程序的垃圾分类系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

流媒体基础解析:视频清晰度的关键因素

在视频处理的过程中&#xff0c;编码解码及码率是影响视频清晰度的关键因素。今天&#xff0c;我们将深入探讨这些概念&#xff0c;并解析它们如何共同作用于视频质量。 编码解码概述 编码&#xff0c;简单来说&#xff0c;就是压缩。视频编码的目的是将原始视频数据压缩成较…

grid网格布局

使用flex布局的痛点 如果使用justify-content: space-between;让子元素两端对齐&#xff0c;自动分配中间间距&#xff0c;假设一行4个&#xff0c;如果每一行都是4的倍数那没任何问题&#xff0c;但如果最后一行是2、3个的时候就会出现下面的状况&#xff1a; /* flex布局 两…

Vehicle HAL(2)--Vehicle HAL 的启动

目录 1. VehicleService-main 函数分析 2. 构建EmulatedVehicleHal 2.1 EmulatedVehicleHal::EmulatedVehicleHal(xxx) 2.2 EmulatedVehicleHal::initStaticConfig() 2.3 EmulatedVehicleHal::onPropertyValue() 3. 构建VehicleEmulator 4. 构建VehicleHalManager (1)初…

【C语言】详解 指针

前言&#xff1a; 在学习指针前&#xff0c;通过比喻的方法&#xff0c;让大家知道指针的作用。 想象一下&#xff0c;你在一栋巨大的图书馆里找一本书。如果没有书架编号和目录&#xff0c;这几乎是不可能完成的任务。 在 C 语言中&#xff0c;指针就像是图书馆的索引系统&…

RabbitMQ仲裁队列高可用架构解析

#作者&#xff1a;闫乾苓 文章目录 概述工作原理1.节点之间的交互2.消息复制3.共识机制4.选举领导者5.消息持久化6.自动故障转移 集群环境节点管理仲裁队列增加集群节点重新平衡仲裁队列leader所在节点仲裁队列减少集群节点 副本管理add_member 在给定节点上添加仲裁队列成员&…

Apache Kafka 实现原理深度解析:生产、存储与消费全流程

Apache Kafka 实现原理深度解析&#xff1a;生产、存储与消费全流程 引言 Apache Kafka 作为分布式流处理平台的核心&#xff0c;其高吞吐、低延迟、持久化存储的设计使其成为现代数据管道的事实标准。本文将从消息生产、持久化存储、消息消费三个阶段拆解 Kafka 的核心实现原…

Python 训练营打卡 Day 41

简单CNN 一、数据预处理 在图像数据预处理环节&#xff0c;为提升数据多样性&#xff0c;可采用数据增强&#xff08;数据增广&#xff09;策略。该策略通常不改变单次训练的样本总数&#xff0c;而是通过对现有图像进行多样化变换&#xff0c;使每次训练输入的样本呈现更丰富…

leetcode付费题 353. 贪吃蛇游戏解题思路

贪吃蛇游戏试玩:https://patorjk.com/games/snake/ 问题描述 设计一个贪吃蛇游戏,要求实现以下功能: 初始化游戏:给定网格宽度、高度和食物位置序列移动操作:根据指令(上、下、左、右)移动蛇头规则: 蛇头碰到边界或自身身体时游戏结束(返回-1)吃到食物时蛇身长度增加…

CCPC dongbei 2025 I

题目链接&#xff1a;https://codeforces.com/gym/105924 题目背景&#xff1a; 给定一个二分图&#xff0c;左图编号 1 ~ n&#xff0c;右图 n 1 ~ 2n&#xff0c;左图的每个城市都会与右图的某个城市犯冲&#xff08;每个城市都只与一个城市犯冲&#xff09;&#xff0c;除…

系统性学习C语言-第十三讲-深入理解指针(3)

系统性学习C语言-第十三讲-深入理解指针&#xff08;3&#xff09; 1. 数组名的理解2. 使用指针访问数组3. ⼀维数组传参的本质4. 冒泡排序5. ⼆级指针 6. 指针数组7. 指针数组模拟二维数组 1. 数组名的理解 在上⼀个章节我们在使用指针访问数组的内容时&#xff0c;有这样的代…

贪心算法实战篇2

文章目录 前言序列问题摆动序列单调递增的数字 贪心解决股票问题买卖股票的最佳时机II 两个维度权衡问题分发糖果根据身高重建队列 前言 今天继续带大家进行贪心算法的实战篇2&#xff0c;本章注意来解答一些运用贪心算法的中等的问题&#xff0c;大家好好体会&#xff0c;怎么…

Java 大视界 -- Java 大数据机器学习模型在元宇宙虚拟场景智能交互中的关键技术(239)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

高速串行接口

1.网口设计方案 上图中给出了两种网口设计方案&#xff0c;最上面是传统设计方式&#xff0c;下面是利用GT作为PHY层的设计&#xff0c;然后FPGA中设计协议层和MAC层。 2.SRIO SRIO的本地操作和远程操作 3.其他高速接口 srio rapid io aurora8b10b aurora64b66b pcie s…

学习STC51单片机23(芯片为STC89C52RCRC)

每日一言 成功的路上从不拥挤&#xff0c;因为坚持的人不多&#xff0c;你要做那个例外。 通过单片机发指令给ESP8266进行通信 通信原理(也是接线原理) 代码如下 代码解释一下&#xff0c;因为我们的指令是字符数组&#xff08;c语言没有字符串的概念&#xff09;&#xff0c;…

一个完整的日志收集方案:Elasticsearch + Logstash + Kibana+Filebeat (一)

整体链路 [应用服务器] --> [Filebeat] --> [Logstash] --> [Elasticsearch] --> [Kibana] 组件职责 Kibana&#xff1a; 可视化和分析日志数据Elasticsearch&#xff1a; 存储和索引日志数据Logstash&#xff1a; 解析、转换和丰富日志数据Filebeat&#xff1a…

网络系统中安全漏洞扫描为何重要?扫描啥?咋扫描?

在网络系统中&#xff0c;安全漏洞扫描占据着极其重要的位置&#xff0c;这一环节有助于我们发现并消除潜在的安全隐患&#xff0c;进而提高网络安全防护的等级。下面&#xff0c;我将对此进行详尽的说明。 基本概念 漏洞扫描技术可以揭示并评估网站存在的安全风险&#xff0…

Socket 编程 TCP

目录 1. TCP socket API 详解 1.1 socket 1.2 bind 1.3 listen 1.4 accept 1.5 read&&write 1.6 connect 1.7 recv 1.8 send 1.9 popen 1.10 fgets 2. EchoServer 3. 多线程远程命令执行 4. 引入线程池版本翻译 5. 验证TCP - windows作为client访问Linu…

基于TMC5160堵转检测技术的夹紧力控制系统设计与实现

点击下面图片带您领略全新的嵌入式学习路线 &#x1f525;爆款热榜 90万阅读 1.6万收藏 一、技术背景与系统原理 在工业自动化领域&#xff0c;夹紧力控制是精密装配、机床夹具等场景的核心需求。传统方案多采用压力传感器伺服电机的闭环控制方式&#xff0c;但存在系统复杂…

XCTF-web-fileclude

解析如下 <?php include("flag.php"); // 包含敏感文件&#xff08;通常包含CTF挑战的flag&#xff09; highlight_file(__FILE__); // 高亮显示当前PHP文件源代码&#xff08;方便查看代码逻辑&#xff09;if(isset($_GET["file1"]…