MST

星途 面试题库

面试题:Java CompletableFuture thenRun方法在高并发复杂业务中的优化策略

在一个高并发且业务逻辑复杂的系统中,使用CompletableFuture的thenRun方法可能会遇到性能瓶颈或资源竞争问题。请阐述你会采取哪些优化策略来解决这些问题,包括但不限于线程池的配置、任务编排方式等,并给出相应的理论依据和代码示例。
21.0万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

优化策略

  1. 合理配置线程池
    • 理论依据:高并发场景下,使用默认的ForkJoinPool可能无法满足业务需求。通过自定义线程池,可以根据系统资源(如CPU核心数、内存等)和业务特性(如I/O密集型或CPU密集型任务)来合理分配线程资源,避免线程过多导致的上下文切换开销和资源竞争。对于CPU密集型任务,线程数一般设置为CPU核心数 + 1;对于I/O密集型任务,线程数可以设置得更高,如CPU核心数 * 2等,具体数值需通过性能测试确定。
    • 代码示例
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class CompletableFutureOptimization {
    private static final int CPU_CORES = Runtime.getRuntime().availableProcessors();
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            CPU_CORES * 2,
            CPU_CORES * 2,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1000),
            new ThreadFactory() {
                private final AtomicInteger threadNumber = new AtomicInteger(1);
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r, "Custom-Thread-" + threadNumber.getAndIncrement());
                    thread.setDaemon(false);
                    return thread;
                }
            },
            new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> {
            // 模拟一些计算
            return "result";
        }, executor)
       .thenRun(() -> {
            // 后续任务
            System.out.println("任务完成后的操作");
        }, executor);
    }
}
  1. 优化任务编排方式
    • 理论依据:减少不必要的依赖和链式调用。如果任务之间的依赖关系复杂,可以将任务拆分成更细粒度的独立任务,并行执行,然后通过CompletableFuture.allOf等方法进行合并,这样可以充分利用多核CPU的性能,提高整体的执行效率。
    • 代码示例
import java.util.concurrent.*;

public class TaskOrchestrationOptimization {
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors() * 2,
            Runtime.getRuntime().availableProcessors() * 2,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1000));

    public static void main(String[] args) {
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            // 模拟任务1
            return "task1 result";
        }, executor);
        CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
            // 模拟任务2
            return "task2 result";
        }, executor);

        CompletableFuture<Void> combinedTask = CompletableFuture.allOf(task1, task2);
        combinedTask.thenRun(() -> {
            try {
                System.out.println("任务1结果: " + task1.get());
                System.out.println("任务2结果: " + task2.get());
                System.out.println("所有任务完成后的操作");
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }, executor);
    }
}
  1. 避免在thenRun中执行长时间操作
    • 理论依据thenRun方法通常用于执行一些轻量级的后续操作。如果在thenRun中执行长时间的计算或I/O操作,会阻塞线程,影响整个系统的并发性能。应该将长时间操作拆分出来,使用异步方式执行。
    • 代码示例
import java.util.concurrent.*;

public class AvoidLongRunningInThenRun {
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors() * 2,
            Runtime.getRuntime().availableProcessors() * 2,
            0L,
            TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1000));

    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> {
            // 前置任务
            return "前置任务结果";
        }, executor)
       .thenRun(() -> {
            // 轻量级后续操作
            System.out.println("轻量级后续操作");
        }, executor)
       .thenRunAsync(() -> {
            // 长时间操作,使用异步方式
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println("长时间异步操作完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, executor);
    }
}