MST

星途 面试题库

面试题:Java Executor框架自定义线程池实现优化策略

假设你需要在高并发场景下使用Executor框架自定义线程池,且系统对响应时间和资源利用率都有较高要求。请详细描述你会采取哪些优化策略来实现一个高效的自定义线程池,并说明这些策略在Java代码层面如何实现。
25.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

优化策略

  1. 合理配置线程池参数
    • 核心线程数:根据任务类型和系统资源确定。对于CPU密集型任务,核心线程数可设置为CPU核心数 + 1;对于I/O密集型任务,核心线程数可设置为CPU核心数 * 2 或更高,具体根据I/O等待时间与CPU计算时间的比例调整。
    • 最大线程数:设置一个合理的上限,避免过多线程导致系统资源耗尽。通常根据系统可用内存、CPU核心数以及任务执行特点来确定。
    • 队列容量:选择合适的任务队列。无界队列(如LinkedBlockingQueue)可防止任务拒绝,但可能导致内存耗尽;有界队列(如ArrayBlockingQueue)可以控制内存使用,但需要合理设置容量,避免任务拒绝。
  2. 线程复用:利用线程池本身的线程复用机制,减少线程创建和销毁的开销。Executor框架中的线程池默认支持线程复用。
  3. 任务优先级处理:如果任务有不同的优先级,可以使用PriorityBlockingQueue作为任务队列,并为任务实现Comparable接口,按照优先级排序。高优先级任务优先执行,从而提高整体响应时间。
  4. 线程池监控与动态调整
    • 监控:通过ThreadPoolExecutor提供的方法(如getActiveCountgetCompletedTaskCount等)实时监控线程池的运行状态,包括活动线程数、已完成任务数、队列大小等。
    • 动态调整:根据监控数据动态调整线程池参数。例如,当队列中的任务数量持续增加且接近队列容量时,可以适当增加线程数;当活动线程数过多且队列中任务很少时,可以适当减少线程数。
  5. 避免线程上下文切换开销:尽量减少锁的使用,使用无锁数据结构(如ConcurrentHashMap)。对于一些必须使用锁的场景,使用粒度更小的锁,减少锁的竞争。

Java代码实现

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CustomThreadPool {
    // 假设CPU核心数为4
    private static final int CPU_CORES = Runtime.getRuntime().availableProcessors();
    // 核心线程数,假设为I/O密集型任务,设置为CPU核心数的2倍
    private static final int CORE_POOL_SIZE = CPU_CORES * 2;
    // 最大线程数
    private static final int MAX_POOL_SIZE = CPU_CORES * 4;
    // 队列容量
    private static final int QUEUE_CAPACITY = 100;
    // 线程存活时间
    private static final long KEEP_ALIVE_TIME = 10;

    public static void main(String[] args) {
        // 使用PriorityBlockingQueue作为任务队列,支持任务优先级
        BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<>();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                taskQueue,
                new ThreadPoolExecutor.CallerRunsPolicy());

        // 提交任务
        for (int i = 0; i < 10; i++) {
            executor.submit(new Task(i));
        }

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

    // 实现具有优先级的任务
    static class Task implements Runnable, Comparable<Task> {
        private final int priority;

        Task(int priority) {
            this.priority = priority;
        }

        @Override
        public void run() {
            System.out.println("Task with priority " + priority + " is running.");
        }

        @Override
        public int compareTo(Task other) {
            return Integer.compare(this.priority, other.priority);
        }
    }
}

在上述代码中:

  1. 首先根据系统CPU核心数设置了核心线程数和最大线程数。
  2. 使用PriorityBlockingQueue作为任务队列,使任务能够按照优先级执行。
  3. 定义了实现Comparable接口的Task类,以便在PriorityBlockingQueue中按优先级排序。
  4. 展示了如何提交任务并关闭线程池,同时处理了线程池关闭过程中的异常。