面试官问我:“Java 中的线程池怎么用, 为什么不用 new Thread?”

72 阅读3分钟

一道看似简单的老题,背后藏着性能优化、资源控制、架构思想。


🪪 起因:一次“内存暴涨”事故

还记得我刚转岗去新项目那阵子,遇到一个非常“积极”的功能同事:
他说后台要同时给几百个用户发送短信通知,于是写了个循环,每次都 new Thread(() -> sendSms()).start()

刚上线的第一天,系统内存暴涨、CPU 飙高,短信也发送失败大半。

一看代码,我差点没背过去:居然直接一口气 new 几百个线程。

而这也正是今天这道面试题的核心所在。


❓ 面试题:Java 中的线程池怎么用?为什么不用 new Thread


🧩 第一步:new Thread 的问题到底在哪?

直接用 new Thread() 虽然简单,但存在几个关键问题:

问题点描述
✅ 无法复用每次执行任务都要重新创建线程,频繁创建销毁线程开销极大。
✅ 不可控无法限制线程数量,来多少任务就 new 多少线程,容易导致 OOM 或线程爆炸。
✅ 无监控机制没法知道当前有多少线程在运行、排队。

一个典型的问题就是:

for (int i = 0; i < 1000; i++) {    new Thread(() -> doTask()).start();}

看着简单,实则危险:线程创建成本高,JVM 的线程栈空间是有限的,轻则 CPU 拉满,重则内存溢出。

🧠 第二步:线程池的本质是什么?

线程池(ThreadPool)是 Java 提供的线程复用机制,它的目标:

  • 控制线程的数量
  • 降低线程频繁创建/销毁的资源开销
  • 提供任务缓存队列、拒绝策略、监控等机制

核心类是:

java.util.concurrent.ThreadPoolExecutor

⚙️ 第三步:线程池的关键参数理解

new ThreadPoolExecutor(    
        corePoolSize,      // 核心线程数    
        maximumPoolSize,   // 最大线程数    
        keepAliveTime,     // 空闲线程最大存活时间    
        unit,              // 时间单位    
        workQueue,         // 任务队列    
        threadFactory,     // 线程工厂   
        handler            // 拒绝策略)

这些参数的组合,决定了线程池的行为模型。

举个面试喜欢问的例子:

如果核心线程数是 10,最大线程数是 100,队列长度是 1000,会出现什么现象?

⏱️ 答案:

  • 前 10 个任务直接执行(core)
  • 11 ~ 1010 个进入队列(queue)
  • 超过 1010,才尝试扩充线程到最大(max)
  • 超过 1100,会触发拒绝策略

这就是线程池“分级处理任务”的设计理念。


✅ 第四步:实际项目中怎么用?

我们通常不会自己手写上面的构造器,而是用工具类 Executors 来快速构造:

ExecutorService executor = Executors.newFixedThreadPool(10);executor.submit(() -> doSomething());

⚠️ 但注意:Executors 工厂方法也不是完全推荐使用的。

比如:

Executors.newCachedThreadPool()

默认最大线程数是 

Integer.MAX_VALUE,可能导致线程过多 —— 慎用!


💡 正确方式:推荐手动构造线程池

ThreadPoolExecutor pool = new ThreadPoolExecutor(10,20,60L,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

这里我们设定了核心数量、最大线程数、队列容量和拒绝策略。这样:

  • 线程数量有边界
  • 队列可控,避免 OOM
  • 拒绝策略可监控异常情况

👀 那么如何选择线程池参数?

我在项目中常用以下方法估算:

线程数 ≈ CPU 核心数 * (1 + 平均等待时间 / 计算时间)

这也是《阿里巴巴 Java 开发手册》中提到的经典经验公式。

举例:

  • I/O 密集型任务(如访问外部服务):线程数要略多
  • CPU 密集型任务(如压缩、加密):线程数与核心数相当

📌 面试建议总结

这道题看似是考你“API”,其实是考你是否:

  • 理解线程池背后的动机和设计理念
  • 知道怎么用线程池去解决实际问题
  • 能举出自己真实使用过线程池的例子

📢 建议答题结构:

1. new Thread 的问题
2. 线程池的优势
3. ThreadPoolExecutor 参数解析
4. 结合自己项目谈谈线程池配置实践

有真实经验,讲起来才有说服力!