Skip to content

线程池面试题

1. 线程池的基本概念

问题:什么是线程池?为什么要使用线程池?

答案

  • 线程池:管理多个线程的容器,可以重复使用线程。
  • 作用
    • 减少线程创建和销毁的开销。
    • 控制并发线程数。
    • 提高系统响应速度。
    • 提高线程的可管理性。

2. 线程池的参数

问题:线程池有哪些重要参数?

答案

  • corePoolSize:核心线程数,线程池中常驻的线程数。
  • maximumPoolSize:最大线程数,线程池中允许的最大线程数。
  • keepAliveTime:空闲线程的存活时间。
  • unit:keepAliveTime的时间单位。
  • workQueue:任务队列,用于存储等待执行的任务。
  • threadFactory:线程工厂,用于创建线程。
  • handler:拒绝策略,当任务队列满且线程数达到最大值时的处理策略。

3. 线程池的执行流程

问题:线程池的执行流程是怎样的?

答案

  1. 如果线程数小于corePoolSize,创建新线程执行任务。
  2. 如果线程数等于corePoolSize,将任务放入队列。
  3. 如果队列已满且线程数小于maximumPoolSize,创建新线程执行任务。
  4. 如果队列已满且线程数等于maximumPoolSize,执行拒绝策略。

4. 线程池的拒绝策略

问题:线程池有哪些拒绝策略?

答案

  • AbortPolicy:抛出RejectedExecutionException异常(默认)。
  • CallerRunsPolicy:由调用线程执行任务。
  • DiscardPolicy:丢弃任务,不抛出异常。
  • DiscardOldestPolicy:丢弃队列中最老的任务,然后重新提交任务。

5. 线程池的创建方式

问题:如何创建线程池?

答案

  • 使用Executors工厂类
java
ExecutorService executor = Executors.newFixedThreadPool(10);
ExecutorService executor = Executors.newCachedThreadPool();
ExecutorService executor = Executors.newSingleThreadExecutor();
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
  • 使用ThreadPoolExecutor
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    10, // corePoolSize
    20, // maximumPoolSize
    60, // keepAliveTime
    TimeUnit.SECONDS, // unit
    new ArrayBlockingQueue<>(100), // workQueue
    Executors.defaultThreadFactory(), // threadFactory
    new ThreadPoolExecutor.AbortPolicy() // handler
);

6. FixedThreadPool

问题:FixedThreadPool的特点是什么?

答案

  • 特点
    • 核心线程数和最大线程数相同。
    • 使用无界队列。
    • 线程数固定,不会创建新线程。
    • 适合负载稳定的场景。

示例

java
ExecutorService executor = Executors.newFixedThreadPool(10);

7. CachedThreadPool

问题:CachedThreadPool的特点是什么?

答案

  • 特点
    • 核心线程数为0,最大线程数为Integer.MAX_VALUE。
    • 使用SynchronousQueue。
    • 线程空闲60秒后回收。
    • 适合负载不稳定的场景。

示例

java
ExecutorService executor = Executors.newCachedThreadPool();

8. SingleThreadExecutor

问题:SingleThreadExecutor的特点是什么?

答案

  • 特点
    • 核心线程数和最大线程数都为1。
    • 使用无界队列。
    • 保证任务按顺序执行。
    • 适合需要顺序执行任务的场景。

示例

java
ExecutorService executor = Executors.newSingleThreadExecutor();

9. ScheduledThreadPool

问题:ScheduledThreadPool的特点是什么?

答案

  • 特点
    • 可以执行定时任务。
    • 可以执行周期性任务。
    • 使用DelayedWorkQueue。

示例

java
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);

// 延迟执行
executor.schedule(() -> {
    System.out.println("Delayed task");
}, 1, TimeUnit.SECONDS);

// 周期性执行
executor.scheduleAtFixedRate(() -> {
    System.out.println("Fixed rate task");
}, 0, 1, TimeUnit.SECONDS);

// 周期性执行(延迟计算)
executor.scheduleWithFixedDelay(() -> {
    System.out.println("Fixed delay task");
}, 0, 1, TimeUnit.SECONDS);

10. 线程池的关闭

问题:如何关闭线程池?

答案

  • shutdown:不再接受新任务,等待已提交的任务执行完毕。
  • shutdownNow:不再接受新任务,尝试停止正在执行的任务,返回未执行的任务列表。

示例

java
executor.shutdown();
executor.shutdownNow();

// 等待线程池关闭
executor.awaitTermination(60, TimeUnit.SECONDS);

11. 线程池的监控

问题:如何监控线程池?

答案

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

// 获取线程池状态
int corePoolSize = executor.getCorePoolSize();
int maximumPoolSize = executor.getMaximumPoolSize();
int poolSize = executor.getPoolSize();
int activeCount = executor.getActiveCount();
long completedTaskCount = executor.getCompletedTaskCount();
int queueSize = executor.getQueue().size();

// 打印线程池状态
System.out.println("Core Pool Size: " + corePoolSize);
System.out.println("Maximum Pool Size: " + maximumPoolSize);
System.out.println("Pool Size: " + poolSize);
System.out.println("Active Count: " + activeCount);
System.out.println("Completed Task Count: " + completedTaskCount);
System.out.println("Queue Size: " + queueSize);

12. 线程池的配置

问题:如何合理配置线程池?

答案

  • CPU密集型任务
    • 线程数 = CPU核心数 + 1
  • IO密集型任务
    • 线程数 = CPU核心数 * 2
  • 混合型任务
    • 根据实际情况调整

示例

java
int cpuCoreCount = Runtime.getRuntime().availableProcessors();
int threadCount = cpuCoreCount + 1; // CPU密集型
int threadCount = cpuCoreCount * 2; // IO密集型

13. 线程池的任务队列

问题:线程池的任务队列有哪些?

答案

  • ArrayBlockingQueue:有界队列,基于数组。
  • LinkedBlockingQueue:无界队列,基于链表。
  • SynchronousQueue:不存储元素,直接传递。
  • PriorityBlockingQueue:优先级队列。
  • DelayQueue:延迟队列。

14. 线程池的线程工厂

问题:如何自定义线程工厂?

答案

java
ThreadFactory threadFactory = new ThreadFactory() {
    private AtomicInteger threadNumber = new AtomicInteger(1);
    
    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread(r, "my-thread-" + threadNumber.getAndIncrement());
        thread.setDaemon(false);
        thread.setPriority(Thread.NORM_PRIORITY);
        return thread;
    }
};

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    10, 20, 60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(100),
    threadFactory,
    new ThreadPoolExecutor.AbortPolicy()
);

15. 线程池的异常处理

问题:如何处理线程池中的异常?

答案

java
ExecutorService executor = Executors.newFixedThreadPool(10);

executor.submit(() -> {
    try {
        throw new RuntimeException("Exception");
    } catch (Exception e) {
        e.printStackTrace();
    }
});

// 使用Future
Future<?> future = executor.submit(() -> {
    throw new RuntimeException("Exception");
});
try {
    future.get();
} catch (Exception e) {
    e.printStackTrace();
}

// 使用UncaughtExceptionHandler
ThreadFactory threadFactory = r -> {
    Thread thread = new Thread(r);
    thread.setUncaughtExceptionHandler((t, e) -> {
        e.printStackTrace();
    });
    return thread;
};

16. 线程池的Future

问题:Future的作用是什么?

答案

  • Future:表示异步计算的结果。
  • 作用
    • 检查任务是否完成。
    • 获取任务结果。
    • 取消任务。

示例

java
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "Result";
});

// 检查任务是否完成
boolean isDone = future.isDone();

// 获取任务结果
String result = future.get();

// 取消任务
boolean isCancelled = future.cancel(true);

17. 线程池的CompletableFuture

问题:CompletableFuture的作用是什么?

答案

  • CompletableFuture:支持链式调用的Future。
  • 作用
    • 支持回调。
    • 支持组合。
    • 支持异常处理。

示例

java
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    return "Hello";
});

future.thenApplyAsync(s -> s + " World")
      .thenAcceptAsync(System.out::println)
      .exceptionally(e -> {
          e.printStackTrace();
          return null;
      });

18. 线程池的ForkJoinPool

问题:ForkJoinPool的作用是什么?

答案

  • ForkJoinPool:用于执行分治任务的线程池。
  • 特点
    • 工作窃取算法。
    • 适合递归任务。
    • 适合大数据量计算。

示例

java
ForkJoinPool forkJoinPool = new ForkJoinPool();

RecursiveTask<Integer> task = new RecursiveTask<Integer>() {
    @Override
    protected Integer compute() {
        if (n <= 1) {
            return n;
        }
        RecursiveTask<Integer> left = new RecursiveTask<Integer>() {
            @Override
            protected Integer compute() {
                return compute(n - 1);
            }
        };
        RecursiveTask<Integer> right = new RecursiveTask<Integer>() {
            @Override
            protected Integer compute() {
                return compute(n - 2);
            }
        };
        left.fork();
        right.fork();
        return left.join() + right.join();
    }
};

Integer result = forkJoinPool.invoke(task);

19. 线程池的饱和策略

问题:如何自定义线程池的饱和策略?

答案

java
RejectedExecutionHandler handler = new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("Task rejected");
    }
};

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    10, 20, 60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(100),
    Executors.defaultThreadFactory(),
    handler
);

20. 线程池的动态调整

问题:如何动态调整线程池的参数?

答案

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

// 调整核心线程数
executor.setCorePoolSize(20);

// 调整最大线程数
executor.setMaximumPoolSize(30);

// 调整空闲线程存活时间
executor.setKeepAliveTime(120, TimeUnit.SECONDS);

// 调整线程池大小
executor.prestartAllCoreThreads();

21. 线程池的预热

问题:如何预热线程池?

答案

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

// 预热核心线程
executor.prestartAllCoreThreads();

// 预热单个核心线程
executor.prestartCoreThread();

22. 线程池的优雅停机

问题:如何优雅停机线程池?

答案

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

// 不再接受新任务
executor.shutdown();

// 等待任务执行完毕
while (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
    System.out.println("Waiting for tasks to complete...");
}

// 强制停机
List<Runnable> unfinishedTasks = executor.shutdownNow();

23. 线程池的内存泄漏

问题:如何避免线程池的内存泄漏?

答案

  • 避免使用无界队列:使用有界队列。
  • 避免任务阻塞:设置合理的超时时间。
  • 避免线程泄漏:及时关闭线程池。
  • 避免任务持有外部引用:使用弱引用。

24. 线程池的监控指标

问题:线程池有哪些监控指标?

答案

  • 核心线程数:corePoolSize
  • 最大线程数:maximumPoolSize
  • 当前线程数:poolSize
  • 活跃线程数:activeCount
  • 已完成任务数:completedTaskCount
  • 队列大小:queueSize
  • 任务总数:taskCount

25. 线程池的最佳实践

问题:使用线程池的最佳实践有哪些?

答案

  • 使用ThreadPoolExecutor而不是Executors。
  • 设置合理的线程池参数。
  • 使用有界队列避免内存溢出。
  • 设置合理的拒绝策略。
  • 监控线程池的状态。
  • 及时关闭线程池。
  • 使用Future获取任务结果。
  • 使用CompletableFuture实现异步编程。
  • 使用ForkJoinPool处理分治任务。
  • 避免在任务中阻塞线程。