面试官:讲讲Java中线程池的核心参数和工作流程?谢飞机:这题我会!

50 阅读4分钟

面试官:讲讲Java中线程池的核心参数和工作流程?谢飞机:这题我会!

面试官:你好,谢飞机,请坐。我看到你简历上写了熟悉Java并发编程,那我们来聊聊线程池吧。

谢飞机:(挺起胸膛)哎哟,这个我熟!ThreadPoolExecutor,我天天用!

面试官:好,那你说说,Java中线程池的核心参数有哪些?

谢飞机:嗯……有核心线程数、最大线程数、一个队列,还有……还有个线程工厂!对,就这四个!

面试官:不错,基本答出来了。那这四个参数分别起什么作用?

谢飞机:核心线程数嘛,就是最少要开几个线程干活;最大线程数就是最多不能超过多少个线程;队列就是任务太多的时候先放那儿排队;线程工厂就是用来创建新线程的——比如可以给线程起名字,方便排查问题!

面试官:很好,那如果任务提交进来,线程池是怎么处理的?

谢飞机:这个更简单了!首先看有没有空闲线程,没有的话就新建线程直到达到核心线程数;然后任务就进队列;队列满了,就继续开线程直到最大线程数;再满了……哦,那就拒绝呗,抛异常!

面试官:嗯,说得不错。那你再说说常见的阻塞队列有哪些?

谢飞机:ArrayBlockingQueue、LinkedBlockingQueue……还有……PriorityBlockingQueue!对,优先级队列!

面试官:可以。那如果使用的是无界队列,比如 LinkedBlockingQueue,默认容量是多少?会有什么风险?

谢飞机:默认……应该是 Integer.MAX_VALUE 吧?风险嘛……内存爆了?(挠头)好像听说过 OOM……

面试官:对,有可能导致内存溢出。那你平时项目里怎么配置线程池的?

谢飞机:呃……我们用 @Async 注解,好像也没配啥……默认的吧……

面试官:(微笑)建议你在实际项目中还是要根据业务场景合理设置参数,避免资源耗尽。

谢飞机:明白明白,回家就改!

面试官:今天先到这里,你的基础还行,回去等通知吧。


【答案详解】:Java线程池核心参数与工作流程

一、ThreadPoolExecutor 的七大构造参数

public ThreadPoolExecutor(
    int corePoolSize,           // 核心线程数
    int maximumPoolSize,        // 最大线程数
    long keepAliveTime,         // 非核心线程空闲存活时间
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,  // 工作队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)
  1. corePoolSize:核心线程数,即使空闲也不会被回收(除非设置 allowCoreThreadTimeOut)。
  2. maximumPoolSize:最大线程数,线程池允许创建的最大线程数量。
  3. keepAliveTime + unit:非核心线程空闲时的存活时间,超时后会被销毁。
  4. workQueue:用于存放等待执行的任务的阻塞队列。
    • ArrayBlockingQueue:有界队列,需指定大小。
    • LinkedBlockingQueue:通常为无界(默认 Integer.MAX_VALUE),可能导致 OOM。
    • SynchronousQueue:不存储元素,每个插入必须等待另一个线程取出。
    • DelayQueue:延迟执行任务。
  5. threadFactory:自定义线程创建方式,推荐命名以便调试。
  6. handler:当队列满且线程数达上限时的拒绝策略。
    • AbortPolicy(默认):抛出 RejectedExecutionException。
    • CallerRunsPolicy:由提交任务的线程自己执行。
    • DiscardPolicy:静默丢弃任务。
    • DiscardOldestPolicy:丢弃队列中最老的任务,重试提交当前任务。

二、任务提交流程(执行顺序)

  1. 当提交一个新任务时:
    • 若当前运行线程数 < corePoolSize,则创建新线程执行任务(即使有空闲线程)。
    • 否则,尝试将任务加入 workQueue。
    • 若 workQueue 已满,且当前线程数 < maximumPoolSize,则创建非核心线程执行任务。
    • 若线程数已达 maximumPoolSize 且队列已满,则触发拒绝策略。

三、最佳实践建议

  • 避免使用 Executors 工具类创建线程池(如 newFixedThreadPool 使用无界队列,易导致 OOM)。
  • 应显式使用 ThreadPoolExecutor 构造函数,明确参数含义。
  • 根据业务类型选择合适的队列和线程数
    • CPU 密集型:线程数 ≈ CPU 核数。
    • IO 密集型:线程数可适当增大。
  • 为线程池命名,便于日志追踪。
  • 监控线程池状态:活跃线程数、队列大小、已完成任务数等。

小贴士:Spring 中可通过 @EnableAsync + 自定义 TaskExecutor 实现可控的异步线程池管理。