MST

星途 面试题库

面试题:Java CompletableFuture thenApplyAsync 方法中的线程池使用

在使用 CompletableFuture 的 thenApplyAsync 方法时,如何自定义线程池来执行异步操作?请编写代码实现,要求在一个场景中,有多个 CompletableFuture 任务,每个任务都通过 thenApplyAsync 方法进行异步转换,并且使用自定义的线程池,同时解释自定义线程池在这种场景下相较于默认线程池的优势和适用场景。
34.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class CompletableFutureCustomThreadPoolExample {

    public static void main(String[] args) {
        // 创建自定义线程池
        ExecutorService executorService = new ThreadPoolExecutor(
                5,
                10,
                10L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100),
                new ThreadFactory() {
                    private final AtomicInteger threadNumber = new AtomicInteger(1);
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r, "CustomThreadPool-" + threadNumber.getAndIncrement());
                        thread.setDaemon(false);
                        return thread;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        // 创建多个CompletableFuture任务
        CompletableFuture.supplyAsync(() -> "Task 1", executorService)
               .thenApplyAsync(result -> result + " processed", executorService)
               .thenAcceptAsync(System.out::println, executorService);

        CompletableFuture.supplyAsync(() -> "Task 2", executorService)
               .thenApplyAsync(result -> result + " processed", executorService)
               .thenAcceptAsync(System.out::println, executorService);

        // 关闭线程池
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
                if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("Pool did not terminate");
                }
            }
        } catch (InterruptedException ie) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

自定义线程池相较于默认线程池的优势:

  1. 资源控制:可以精确控制线程池的大小、队列容量等参数。例如,通过设置核心线程数和最大线程数,可以避免线程过多导致系统资源耗尽,也可以避免线程过少导致任务处理效率低下。在上述代码中,核心线程数设为5,最大线程数设为10,根据任务负载可以更好地利用系统资源。
  2. 线程命名:自定义线程池可以为线程设置有意义的名称,方便在日志和监控中定位问题。如代码中通过自定义ThreadFactory为线程命名为CustomThreadPool-开头,便于排查问题。
  3. 拒绝策略定制:可以选择适合业务场景的拒绝策略。在上述代码中,采用了CallerRunsPolicy策略,当队列满且线程池达到最大线程数时,新任务会在调用者线程中执行,避免任务丢失。而默认线程池可能采用的是丢弃策略,可能不符合某些业务需求。

适用场景:

  1. 资源敏感场景:在服务器资源有限的情况下,通过自定义线程池合理分配资源,避免过多线程消耗过多内存等资源。比如在一个内存有限的嵌入式系统中运行异步任务。
  2. 任务优先级场景:可以基于自定义线程池实现任务优先级队列,优先处理重要任务。虽然上述代码未体现,但在实际应用中可根据业务需求定制。
  3. 业务隔离场景:不同业务模块的异步任务可以使用不同的自定义线程池,实现业务之间的隔离,避免某个业务的高负载影响其他业务。例如,电商系统中订单处理和商品推荐的异步任务可以分别使用不同线程池。