MST

星途 面试题库

面试题:Java CompletableFuture whenComplete与线程池的深度结合应用

假设你有一个复杂的业务场景,需要使用CompletableFuture结合线程池来处理一系列任务,并且在任务完成后通过whenComplete方法进行结果处理。请设计一个完整的方案,说明如何合理配置线程池参数,以及whenComplete方法在这个场景下如何保证数据的一致性和系统的稳定性,同时给出核心代码示例。
25.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

线程池参数配置

  1. 核心线程数(corePoolSize):根据业务场景中预计的常驻任务数量来设置。如果任务数量相对稳定,可设置为该稳定值。例如,若预计同时处理的任务数量通常在10个左右,可将corePoolSize设为10。这能确保这些任务能立即被处理,而无需等待新线程创建。
  2. 最大线程数(maximumPoolSize):考虑业务高峰期可能出现的最大任务并发量。比如,业务高峰期可能有100个任务同时到达,且这些任务处理时间较短,可将maximumPoolSize设为100。要注意,设置过大可能导致系统资源耗尽。
  3. 队列容量(workQueue):根据任务的特性选择合适的队列。如果任务处理时间短且数量多,可选用有界队列,如ArrayBlockingQueue,设置一个合理的容量,如200,防止任务堆积过多占用过多内存。若任务处理时间较长,可使用无界队列LinkedBlockingQueue,但需注意可能导致OOM。
  4. 线程存活时间(keepAliveTime):对于处理时间较短但并发量波动较大的场景,设置一个合适的keepAliveTime,如5秒。当线程数超过corePoolSize时,空闲线程在这个时间后会被销毁,释放资源。

whenComplete保证数据一致性和系统稳定性

  1. 数据一致性whenComplete方法在任务完成后执行,无论是正常完成还是异常结束。通过在whenComplete中进行数据的合并、校验等操作,可以确保最终的数据状态是一致的。例如,如果任务是对数据库进行读写操作,在whenComplete中可以进行事务的提交或回滚,保证数据的完整性。
  2. 系统稳定性whenComplete可以捕获任务执行过程中的异常,避免异常未处理导致系统崩溃。在whenComplete中可以进行异常的记录、重试等操作,增强系统的稳定性。

核心代码示例

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class CompletableFutureExample {
    private static final AtomicInteger counter = new AtomicInteger();

    public static void main(String[] args) {
        // 配置线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                10,
                100,
                5,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(200)
        );

        CompletableFuture.supplyAsync(() -> {
            // 模拟任务执行
            System.out.println("Task " + counter.incrementAndGet() + " is running");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "Task " + counter.get() + " result";
        }, executor)
        .whenComplete((result, ex) -> {
            if (ex != null) {
                System.out.println("Task failed: " + ex.getMessage());
            } else {
                System.out.println("Task completed: " + result);
            }
            // 可以在这里进行数据一致性相关操作,如数据库事务处理等
        });

        // 关闭线程池
        executor.shutdown();
    }
}

在上述代码中:

  1. 首先配置了一个ThreadPoolExecutor,设置了核心线程数、最大线程数、线程存活时间和队列容量。
  2. 使用CompletableFuture.supplyAsync提交任务到线程池执行。
  3. whenComplete方法在任务完成后处理结果,无论是正常完成还是异常,都能确保系统的稳定性和数据一致性。最后关闭线程池。