CompletableFuture并行编程生产问题处理

news2025/1/9 19:41:40

前提

        本文章是在项目中发现一些同学对并行编程的思想理解,或者说对代码的执行逻辑有些理解偏颇的地方。特整理此文章进行分享,希望有同样困惑的小伙伴能够对此类问题有一个清晰的理解。

        在此不会介绍CompletableFuture对库函数用法,因为库函数的用法,有很多人已经总结的很清楚了,大家可以自行搜索。

背景

        最近在公司的一次代码评审的时候,发现一位同学的代码中,出现了在使用CompletableFuture的一个问题。

 场景

        本地方法,需要同时调用多家自营商户,获取商品的价格信息。如果哪家平台出现异常(超时、业务异常)场景,则本地方法不向下游返回该商品数据。

问题

        在调用三个不同的自营平台接口时,诉求时并行执行,总时长为三个平台中交互时间最长的那个,且不可超过500ms,如果超过500ms则丢弃该任务,直接返回其他调用结果。但发现如果调用超过设置的超时阈值500ms时,并没有超时丢弃,而是等待着时间较长的任务结束后一并返回。业务代码如下:

  • 原代码逻辑
public class Parallel_3 {

    private static final Map<Integer, Long> TENANT_SLEEP_CONFIG = new HashMap<>(8);

    static {
        TENANT_SLEEP_CONFIG.put(1, 100L);
        TENANT_SLEEP_CONFIG.put(2, 150L);
        TENANT_SLEEP_CONFIG.put(3, 800L);
    }

    private static List<String> parallelExecuteTask(Set<Integer> tenantIdSet) throws Exception {
        CompletableFuture<String>[] completableFutures = new CompletableFuture[3];
        Iterator<Integer> tenantIdIterator = tenantIdSet.iterator();
        int index = 0;
        while (tenantIdIterator.hasNext()) {
            Integer tenantId = tenantIdIterator.next();
            CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> Parallel_3.executeTask(tenantId));
            completableFutures[index++] = completableFuture;
        }
        try {
            CompletableFuture.allOf(completableFutures).get(500, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            System.err.println("调用任务超时, 不再等待, 直接处理后序逻辑.");
        }
        List<String> resultList = new ArrayList<>();
        for (CompletableFuture<String> completableFuture : completableFutures) {
            resultList.add(completableFuture.get());
        }
        return resultList;
    }

    private static String executeTask(int tenantId) {
        try {
            Thread.sleep(TENANT_SLEEP_CONFIG.get(tenantId));
            System.out.println("current index : " + tenantId);
            return "任务返回值 : " + tenantId;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "";
    }

    private static Set<Integer> getTenantIdSet() {
        Set<Integer> tenantIdSet = new HashSet<>();
        tenantIdSet.add(1);
        tenantIdSet.add(2);
        tenantIdSet.add(3);
        return tenantIdSet;
    }


    public static void main(String[] args) throws Exception {
        // 参数集
        Set<Integer> tenantIdSet = getTenantIdSet();

        // 并行执行任务
        List<String> resultList = parallelExecuteTask(tenantIdSet);

        // 打印结果集合
        System.out.println("result data: " + resultList);

        // 主线程结束
        System.out.println("main thead end ...");
    }
}
  • 打印结果
current index : 1
current index : 2
调用任务超时, 不再等待, 直接处理后序逻辑.
current index : 3
result data: [任务返回值 : 1, 任务返回值 : 2, 任务返回值 : 3]
main thead end ...

我们发现result data中居然还有“任务返回值 :3”,该任务按照预想已经超时,并不应该出现在返回值列表中。 

理解误区

  1. 当多任务初始化完成,执行后,调用如下代码后,即等待全部任务执行完毕,如果有超时任务不在等待。
    CompletableFuture.allOf(completableFutures).get(500, TimeUnit.MILLISECONDS);
  2. 执行完等待全部任务结束,并设置超时时间,再执行如下代码,只会取到未超时的任务的执行结果。
    for (CompletableFuture<String> completableFuture : completableFutures) {
        resultList.add(completableFuture.get());
    }
  3. 问题原因

        第一步,并没有问题,当调用了allOf方法并设置结束时间,如果等到了超时时间,并没有全部任务都执行完毕,则不会继续等待,而是继续执行后续逻辑。

        当执行到第二步的代码逻辑时,循环任务内容并调用get()方法时,则会等待超时的任务执行完毕,并返回任务执行结果,导致外层调用整体超时。

修复方案

  • 上下文对象

        可以声明一个方法的上下文对象,将每个任务的处理结果设置到上下文对象中,在调用allOf方法后,直接返回上下文对象的内容,不在循环调用get方法。

  • getNow(T valueIfAbsent)

        使用getNow方法替换get方法,并设置返回值,如果超时情况,则直接使用默认值。

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

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

相关文章

XSS-labs靶场(超详解)1-20关——附原码

level1 原码 <!DOCTYPE html><!--STATUS OK--><html> <head> <meta http-equiv"content-type" content"text/html;charsetutf-8"> <script> window.alert function() { confirm("完成的不错&#xff0…

【Java】字符串StringBuilder类和Stringjoiner类(013)

目录 ♦️StringBuilder类 &#x1f38f;StringBuilder构造方法 &#x1f421;无参构造 &#x1f421;有参构造 &#x1f38f;SringBuilder常用方法 &#x1f38f;StringBuildre类练习题&#xff1a; &#x1f421;对称字符串 &#x1f421;拼装字符串 ♦️Stringjoine…

(Python)内存管理

前言 内存泄漏在编程中是一个严重的问题&#xff0c;可能导致程序性能下降、系统不稳定甚至崩溃。 目录 危害 风险 动态内存分配 引用计数 内存池 垃圾回收 设计原则 危害 性能下降&#xff1a;可用内存减少&#xff0c;导致系统频繁进行内存交换&#xff0c;使程序运行…

Netdevops入门之Telnetlib语法案例

1、Telnetlib模块&#xff1a; 支持telnet/ssh远程访问的模块很多&#xff0c;常见的有telnetlib、ciscolib、paramiko、netmiko、pexpect,其中telnetlib和ciscolib对应telnet协议&#xff0c;后面3个对应SSH协议。 ①-通过ENSP环境搭建实验环境 ②-基础语法-telnetlib案例1&…

最优控制、轨迹优化相关笔记、感悟

写在前面 上学期和最近无聊在看一些最优控制、轨迹优化相关的内容&#xff0c;涉及到MIT Russ Tedrake教授的Underactuated Robotics&#xff0c;以及CMU Zachary Manchester教授的Optimal Control&#xff08;两套课程其实挺像的&#xff09;&#xff0c;加起来估计也看了十多…

机器学习算法(二)线性模型

一、线性回归 假设自变量X矩阵有3个特征&#xff0c;因变量是Y矩阵&#xff0c;w是系数矩阵 Y X * w 损失函数&#xff1a;误差平方和函数&#xff08;Y - label&#xff09;** 2 二、逻辑回归 线性回归得到的是一个实数值 z &#xff0c;用sigmoid函数可以将其映射到 0 …

【SQL Server 】SQL Server 网络配置

目录 ​编辑 第3章&#xff1a;SQL Server 网络配置 SQL Server 网络监听器 SQL Server 网络协议 配置 SQL Server 网络协议 示例&#xff1a;配置 SQL Server 使用自定义端口 安全注意事项 第3章&#xff1a;SQL Server 网络配置 SQL Server 网络监听器 SQL Server 通…

php时间 cookie session 文件上传基础

时间和日期 PHP Date&#xff08;&#xff09; 函数 格式// date(format,timestamp) format Required. Specifies the format of the timestamp timestamp Optional. Specifies a timestamp. Default is the current date and time d - 表示每月的某一天&#xff08;01…

反序列化漏洞靶机实战-serial

一.安装靶机 下载地址为https://download.vulnhub.com/serial/serial.zip&#xff0c;安装好后开启靶机&#xff0c;这里并不需要我们去登录&#xff0c;直接扫描虚拟机nat模式下c网段的ip&#xff0c;看看哪个的80端口开放&#xff0c;然后直接去访问 二.查找cookie 访问靶…

Java Try学习

一. 介绍 io.vavr.control.Try 是 Vavr 库的一个类&#xff0c;用于处理可能抛出异常的方法调用&#xff1b;它提供了一种优雅的方式来处理成功和失败的情况&#xff0c;而无需显示地使用 try-catch 块&#xff1b; 简而言之&#xff0c;它可以优雅的实现 try-catch&#xff…

链表【4】

目录 链表删除指定值元素 反转链表&#xff08;静态链表型&#xff09; 链表删除指定值元素 错误1&#xff1a;两个if间没有用else&#xff0c;导致两个都执行了 #include<stdio.h> const int N1005; int n,first,firstid; using namespace std; struct node {int da…

【Vue3】作用域插槽

【Vue3】作用域插槽 背景简介开发环境开发步骤及源码 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日子。本文…

UI界面自动化测试-Selenium

Selenium工作原理 SeleniumAPI 定位元素 Selenium操作对象 **send_keys 在对象上模拟按键输入 ** clear 清除对象输入的文本内容 ** click 点击对象(无限制) submit 提交(用于form表单) ** getText() 用于获取元素的文本信息 getAttribute() 用于获取属性的值 quit 关闭…

【奥顺苹果CMS二开泛目录4.X版】PHP站群程序新增首页堆砌关键词新增四套seo模板

演示站&#xff08;赠送四套模板&#xff09;&#xff1a; https://macfan.qdwantong.com https://macfan2.qdwantong.com https://macfan3.qdwantong.com https://macfan4.qdwantong.com 4.X版程序特色功能&#xff1a; 后台除了可以设置干扰码、转码、插入符号和拼音这…

[Bugku] web-CTF-矛盾

1.开启环境 2.根据内容得知&#xff0c;get一个num&#xff0c;若num不是数字&#xff0c;出一次num的值&#xff0c;后若num1出flag&#xff1b;若num为数字则不进行任何操作所以要输出flag&#xff0c;首先要num不是数字&#xff0c;然后又要num1这显然是矛盾的&#xff0c;对…

transform详解

参考&#xff1a;https://zhuanlan.zhihu.com/p/690055241 https://zhuanlan.zhihu.com/p/685724799 https://zhuanlan.zhihu.com/p/609523552 cnn是通过卷积核的方式实现权重偏置的计算&#xff0c;ywkb&#xff0c;激活&#xff0c;前馈神经网络&#xff0c;反向传播。 trans…

P31结构体初阶

结构体的声明 结构体的基础知识 结构是一些值的集合&#xff0c;这些值成为成员变量。结构的每个成员可以是不同类型的变量。 结构体的声明 结构成员的类型 结构的成员可以是标量、数组、指针&#xff0c;甚至是其他结构体 结构体变量的定义和初始化 结构体成员的访问 结构…

AI技能提升学习-免费24年最新甲骨文(OCI)开卷AI证书(有答案)+代码调用OCI生成式AI服务教程

之前好多小伙伴和我反馈错过了24年甲骨文的AI专家级证书免费考试&#xff0c;这次小李哥就给大家带来了24年最新的OCI另外一门AI基础级考试&#xff0c;主要目的是帮助大家提升AI/ML的基础知识和技能&#xff0c;给大家带来免费的学习福利&#xff0c;赶紧关注小李哥不要再错过…

大数据技术原理-spark编程与应用

摘要 本实验总结了在"大数据技术原理"课程中进行的Spark编程实验。实验环境基于Apache Spark&#xff0c;旨在通过实践加深对Spark数据处理能力的理解。实验的主要内容包括开启Spark shell、导入必要的包、读入数据集、数据预处理、聚类模型训练、确定数据模型的中心…

STM32内部Flash存贮数据的应用(STM32F446)

目录 概述 1 STM32内部Flash介绍 1.1 MCU简介 1.2 存储空间 1.3 主要特性 1.4 嵌入式闪存 2 库函数介绍 2.1 编程接口函数 2.2 锁和解锁函数 3 功能实现 3.1 写数据函数&#xff1a;FlashDrv_Write 3.2 读数据函数&#xff1a; FlashDrv_read 3.3 源代码 4 测试…