MST

星途 面试题库

面试题:Java CompletableFuture supplyAsync任务调度中的线程池优化

在Java CompletableFuture的supplyAsync任务调度场景下,默认使用的是ForkJoinPool.commonPool()线程池。请阐述在高并发且任务类型复杂多样的情况下,为什么需要自定义线程池,如何自定义线程池以优化任务调度,并说明自定义线程池时需要考虑哪些关键因素?
20.9万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

为什么需要自定义线程池

  1. 资源竞争:ForkJoinPool.commonPool() 是一个公共线程池,多个不同类型任务可能会竞争资源,导致性能下降。例如,I/O 密集型任务和 CPU 密集型任务共用此线程池,I/O 任务长时间等待 I/O 操作完成,会阻塞 CPU 密集型任务的执行。
  2. 线程饥饿:如果高并发场景下某些任务执行时间长,可能会导致其他任务无法及时获取线程资源,出现线程饥饿现象。
  3. 缺乏针对性优化:不同类型任务对线程池配置需求不同,公共线程池无法满足这种多样性需求。比如大数据处理任务可能需要更多线程,而一些简单的轻量级任务可能适合较少线程的线程池。

如何自定义线程池以优化任务调度

  1. 创建ThreadPoolExecutor:可以使用 ThreadPoolExecutor 类来自定义线程池。例如:
import java.util.concurrent.*;

public class CustomThreadPoolExample {
    public static void main(String[] args) {
        // 核心线程数
        int corePoolSize = 5;
        // 最大线程数
        int maximumPoolSize = 10;
        // 线程存活时间
        long keepAliveTime = 10;
        TimeUnit unit = TimeUnit.SECONDS;
        // 任务队列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(20);
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue);

        CompletableFuture.supplyAsync(() -> {
            // 任务逻辑
            return "Result";
        }, executor);
    }
}
  1. 使用CompletableFuture时传入自定义线程池:在 supplyAsync 方法中,第二个参数传入自定义的 ThreadPoolExecutor 对象,如上述代码中所示。

自定义线程池时需要考虑的关键因素

  1. 核心线程数:核心线程数决定了线程池中常驻的线程数量。应根据任务类型和硬件资源确定,对于 CPU 密集型任务,核心线程数一般设置为 CPU 核心数;对于 I/O 密集型任务,核心线程数可适当增加,因为 I/O 操作等待时线程可以处理其他任务。
  2. 最大线程数:最大线程数限制了线程池能创建的最多线程数量。设置过大可能导致系统资源耗尽,过小则无法充分利用系统资源。需要综合考虑系统的负载能力和任务的并发度。
  3. 线程存活时间:非核心线程在没有任务执行时,等待新任务的存活时间。设置过长会浪费资源,过短则频繁创建和销毁线程带来额外开销。
  4. 任务队列:任务队列用于存放等待执行的任务。不同类型的队列有不同特性,如 ArrayBlockingQueue 是有界队列,LinkedBlockingQueue 可以是无界队列(也可设置有界)。选择合适的队列类型可以避免任务堆积导致内存溢出,或因队列过小导致任务无法及时提交。
  5. 拒绝策略:当任务队列已满且线程池达到最大线程数时,新任务需要有拒绝策略。常见策略包括 AbortPolicy(抛出异常)、CallerRunsPolicy(在调用者线程执行任务)、DiscardPolicy(丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。应根据业务需求选择合适的拒绝策略。