java的异步任务优化

源于实际项目中出现任务执行超时或者出现部分任务异常导致整个业务功能失败(兼容java8版本)

先看代码

package tool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Supplier;

public class CompletableFutureAllOfDemo {
    // 全局定时线程池(用于单个任务超时控制,复用避免频繁创建)
    private static final ScheduledExecutorService TIMEOUT_SCHEDULER = Executors.newScheduledThreadPool(3);
    // 业务线程池(执行异步任务,建议替换为项目自定义线程池)
    private static final ExecutorService BUSINESS_EXECUTOR = Executors.newFixedThreadPool(5);

    /**
     * 封装单个异步任务:含10秒超时 + 全局异常处理
     * @param task 业务任务(Supplier类型,无入参有返回值)
     * @param taskName 任务名称(用于异常/超时提示)
     * @param timeoutDefault 超时默认返回值
     * @param exceptionDefault 异常默认返回值
     * @return 封装后的CompletableFuture
     */
    private static <T> CompletableFuture<T> wrapTask(
            Supplier<T> task,
            String taskName,
            T timeoutDefault,
            T exceptionDefault
    ) {
        // 1. 提交业务异步任务
        CompletableFuture<T> future = CompletableFuture.supplyAsync(task, BUSINESS_EXECUTOR);

        // 2. JDK8实现10秒超时:超时后强制设置默认值
        TIMEOUT_SCHEDULER.schedule(() -> {
            boolean isTimeout = future.complete(timeoutDefault);
            if (isTimeout) {
                System.err.println("任务[" + taskName + "]超时(10秒),返回默认值");
            }
        }, 10, TimeUnit.SECONDS);

        // 3. 全局异常处理:捕获任务执行/回调链所有异常
        return future.handle((result, ex) -> {
            if (ex != null) {
                System.err.println("任务[" + taskName + "]执行异常:" + ex.getMessage());
                return exceptionDefault;
            }
            return result; // 正常结果/超时默认值
        });
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // ========== 步骤1:定义多个业务任务 ==========
        // 任务1:正常执行(模拟耗时5秒)
        Supplier<String> task1 = () -> {
            try { Thread.sleep(5000); } catch (InterruptedException e) {}
            return "任务1正常结果";
        };

        // 任务2:模拟执行异常
        Supplier<String> task2 = () -> {
            throw new RuntimeException("数据库查询失败");
        };

        // 任务3:模拟超时(耗时15秒,超过10秒超时阈值)
        Supplier<String> task3 = () -> {
            try { Thread.sleep(15000); } catch (InterruptedException e) {}
            return "任务3正常结果";
        };

        // ========== 步骤2:封装每个任务(超时+异常处理) ==========
        CompletableFuture<String> future1 = wrapTask(task1, "任务1", "任务1超时默认值", "任务1异常默认值");
        CompletableFuture<String> future2 = wrapTask(task2, "任务2", "任务2超时默认值", "任务2异常默认值");
        CompletableFuture<String> future3 = wrapTask(task3, "任务3", "任务3超时默认值", "任务3异常默认值");

        // ========== 步骤3:使用allOf等待所有任务完成 ==========
        CompletableFuture<Void> allFuture = CompletableFuture.allOf(future1, future2, future3);
        // 阻塞等待所有任务(无论成功/失败/超时)执行完毕
        allFuture.get();

        // ========== 步骤4:收集所有任务的最终结果 ==========
        List<String> results = new ArrayList<>();
        results.add(future1.get()); // 任务1:正常结果
        results.add(future2.get()); // 任务2:异常默认值
        results.add(future3.get()); // 任务3:超时默认值

        // 打印最终结果
        System.out.println("所有任务结果:");
        for (String result : results) {
            System.out.println("- " + result);
        }

        // ========== 步骤5:关闭线程池(项目关闭时执行) ==========
        TIMEOUT_SCHEDULER.shutdown();
        BUSINESS_EXECUTOR.shutdown();
    }
}

输出结果:

优化点说明:

1.由于之前异步任务写得比较简陋,这里做了一个方法提取,增加了10秒超时和全局异常捕获

2.不再在无返回结果的任务中用Map收集结果,而是在结束之后统一收集结果,每一个异步任务结果返回

3.之前用runAsyn改成supplyAsyn

4.不用Executors的方式创建线程池,而用ThreadPoolTaskExecutor方式创建

5.两个线程池隔离分开(全局调度线程池和业务执行线程池),而非共用一个线程池,且用@Autowaired注入类中,通过方法参数传递进去,注意ThreadPoolTaskExecutor 是 Spring 框架(spring-context 模块)提供的线程池实现

隔离的好处:1.资源抢占风险隔离2.故障域隔离3.监控和调优更精准

在 Spring/Spring Boot 项目中,推荐使用 ThreadPoolTaskExecutor(而非原生 ThreadPoolExecutor),因为它更贴合 Spring 生态,支持 Spring 的生命周期管理、线程池监控、优雅关闭等特性。

异步任务

异步任务,默认使用ForkJoinPool.commonPool(),也可指定自定义线程池。

  • 无返回值:CompletableFuture.runAsync(Runnable)
  • 有返回值:CompletableFuture.supplyAsync(Supplier)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值