JAVA-批量异步任务实现

1.批量异步

1.CompletableFuture技术详细描述

什么是 CompletableFuture

  • 定义CompletableFuture 是 Java 8 引入的异步编程工具,用于表示一个可能在未来完成的异步计算的结果。它支持链式操作、组合多个异步任务、异常处理和回调机制。

  • 核心机制

    • 异步执行:通过 supplyAsync 或 runAsync 启动异步任务(默认使用 ForkJoinPool 的公共线程池)。

    • 链式处理:通过 thenApplythenAcceptthenRun 等方法串联任务。

    • 组合任务:通过 allOfanyOf 合并多个任务的结果。

技术核心场景

  • 并行处理:同时执行多个耗时任务(如调用多个外部 API、查询多个数据库)。

  • 结果聚合:在所有任务完成后合并结果。

  • 响应式编程:构建非阻塞、异步的流水线操作。

2.优缺点分析

优点

优点 说明
非阻塞 避免线程阻塞,提升系统吞吐量。
灵活性高 支持链式调用、任务组合、异常处理,适用于复杂异步逻辑。
资源利用高效 使用线程池管理任务,避免频繁创建/销毁线程。
与 Stream 集成 可与 Java Stream API 结合使用,简化集合的并行处理。

缺点

缺点 说明
回调地狱 过多的链式调用可能导致代码可读性下降(需合理设计)。
错误处理复杂 需要显式处理异常(如 exceptionallyhandle)。
线程池管理 默认线程池可能不适用于所有场景,需自定义线程池优化。
调试困难 异步任务的堆栈跟踪可能不直观,增加调试难度。

3.完整实现代码示例

场景:并行查询多个数据库,合并结果。

示例一

import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;

public class CompletableFutureDemo {

    // 模拟数据库查询方法
    private static List queryDatabase(String source) {
        // 模拟耗时操作
        try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
        return List.of(
            new testBaseDTO(source + "-Material1"),
            new testBaseDTO(source + "-Material2")
        );
    }

    public static void main(String[] args) {
        // 1. 创建异步任务列表
        List>> futures = new ArrayList<>();

        // 2. 添加异步任务(并行查询不同数据库)
        futures.add(CompletableFuture.supplyAsync(() -> queryDatabase("DB1")));
        futures.add(CompletableFuture.supplyAsync(() -> queryDatabase("DB2")));
        futures.add(CompletableFuture.supplyAsync(() -> queryDatabase("DB3")));

        // 3. 等待所有任务完成,并合并结果
        CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[0])
        ).join();

        // 4. 处理最终结果

   List resultList = new ArrayList<>();
   for (CompletableFuture> future : futures) {
    try {
        List oneResult = future.get();
        resultList.addAll(oneResult);
    } catch (Exception e) {
        log.error("批量查询异常", e);
    }
  }

        // 5. 阻塞获取最终结果(实际中应避免在主线程阻塞)
        
        System.out.println("合并后的结果数量: " + resultList.size());
        resultList.forEach(dto -> System.out.println(dto.getName()));
    }

    // DTO 定义
    static class testBaseDTO {
        private String name;
        public testBaseDTO(String name) { this.name = name; }
        public String getName() { return name; }
    }
}

示例二

// 创建异步任务列表
List>> futures = new ArrayList<>();

// 添加异步任务(例如:并行查询多个数据库)
futures.add(CompletableFuture.supplyAsync(() -> queryDatabase1()));
futures.add(CompletableFuture.supplyAsync(() -> queryDatabase2()));

// 等待所有任务完成,并合并结果
CompletableFuture allFutures = CompletableFuture.allOf(
    futures.toArray(new CompletableFuture[0])
);

// 最终处理结果
List mergedResults = allFutures.thenApply(v ->
    futures.stream()
        .flatMap(future -> future.join().stream())
        .collect(Collectors.toList())
).join();

4.线程池选择

1. 默认线程池:ForkJoinPool.commonPool()

如果未显式指定线程池,CompletableFuture 默认使用 ForkJoinPool.commonPool()
适用场景

  • 计算密集型任务(如复杂数学计算、数据处理)。

  • 短期任务且任务量较小。

特点

  • 线程池大小为 Runtime.getRuntime().availableProcessors() - 1(如 4 核 CPU 默认 3 线程)。

  • 所有 CompletableFuture 共享同一个公共池,可能导致资源竞争。

2. 自定义线程池

通过显式传递 Executor,可以更精细地控制线程池行为。
适用场景

  • I/O 密集型任务(如网络请求、数据库查询)。

  • 需要隔离任务类型(避免公共池资源耗尽)。

  • 长期运行或阻塞任务。

推荐线程池类型

线程池类型

适用场景

配置建议

ThreadPoolExecutor

通用场景,支持自定义参数

核心线程数根据任务类型调整,队列需有界。

ForkJoinPool

分治任务(如递归、并行流)

工作窃取(Work-Stealing)机制适合任务拆分。

自定义线程池示例

// 创建自定义线程池
ExecutorService customExecutor = new ThreadPoolExecutor(
    10,                        // 核心线程数
    50,                        // 最大线程数
    60L,                       // 空闲线程存活时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(1000), // 有界队列(容量 1000)
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

// 使用自定义线程池
CompletableFuture.supplyAsync(() -> {
    return fetchDataFromDatabase();
}, customExecutor);

3. 线程池配置建议

参数调优

参数

计算密集型任务

I/O 密集型任务

核心线程数

CPU 核心数(或略多)

根据 I/O 等待时间调整(如 2N)

最大线程数

等于核心线程数

较高(如 100~200)

队列类型

同步队列(SynchronousQueue

有界队列(如 LinkedBlockingQueue

拒绝策略

抛异常(AbortPolicy

降级或重试(如 CallerRunsPolicy

队列与拒绝策略

  • 有界队列:防止任务无限堆积导致内存溢出(OOM)。

  • 拒绝策略

    • CallerRunsPolicy:主线程执行被拒绝的任务(避免任务丢失)。

    • DiscardOldestPolicy:丢弃队列中最旧的任务(适合实时性要求高的场景)。

4. 典型场景代码示例

场景 1:高并发 I/O 任务

// 创建 I/O 密集型线程池
ExecutorService ioExecutor = new ThreadPoolExecutor(
    20,                        // 核心线程数(假设有 10 个并发连接)
    200,                       // 最大线程数
    60L,
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(500),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

// 并行调用多个 API
List> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
    futures.add(CompletableFuture.supplyAsync(() -> {
        return callExternalAPI(); // 模拟 I/O 操作
    }, ioExecutor));
}

// 合并结果
CompletableFuture allFutures = CompletableFuture.allOf(
    futures.toArray(new CompletableFuture[0])
);
allFutures.thenRun(() -> {
    List results = futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList());
    System.out.println("Total results: " + results.size());
});

你可能感兴趣的:(java,开发语言,异步,springboot,后端)