高频面试题:说一下线程池吧?(线程池原理,核心参数,创建方式,应用场景都要说到才能让面试官心服口服)
面试合集 订阅面试合集
线程池这个面试题可以说不管你是初/中/高级任何阶段,面试官都爱问,对于这种高频次面试题,我们必须熟记于心。在结合自己项目说一个参数为啥那样配置,基本回答完毕就是加分题了。
接下来看我娓娓道来~~~码字不易,有问题评论区讨论。
一、Java线程池的创建方式5种(先说结果在说过程)
你说出5种的时候,面试官就知道你懂的,就算后面有1,2个忘记了也没关系。
1. 通过 Executors
工具类创建(不推荐用于生产环境)
虽然方便,但阿里巴巴《Java开发手册》明确禁止使用 Executors
直接创建线程池,因为部分方法使用了无界队列,可能导致OOM。
(1)newFixedThreadPool(int nThreads)
-
固定大小线程池
-
使用
LinkedBlockingQueue
(无界队列)
ExecutorService pool = Executors.newFixedThreadPool(5);
(2)newSingleThreadExecutor()
-
单线程线程池
-
内部也是
LinkedBlockingQueue
ExecutorService pool = Executors.newSingleThreadExecutor();
(3)newCachedThreadPool()
-
可缓存线程池,线程数可无限增长(
Integer.MAX_VALUE
) -
使用
SynchronousQueue
ExecutorService pool = Executors.newCachedThreadPool();
(4)newScheduledThreadPool(int corePoolSize)
-
支持定时/周期性任务执行
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
⚠️ 为什么不推荐?
FixedThreadPool
和
SingleThreadExecutor
使用无界队列,任务堆积可能导致 OOMCachedThreadPool
线程数无上限,可能创建过多线程,耗尽系统资源
✅ 2. 通过 ThreadPoolExecutor
构造函数创建(推荐!生产环境标准做法)
publicThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler
)
示例(推荐写法):
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60L, // 非核心线程空闲存活时间TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(100), // 有界阻塞队列Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
二、线程池的七大核心参数详解
参数 | 类型 | 说明 |
---|---|---|
1. corePoolSize | int | 核心线程数。即使空闲也不会被回收(除非设置 |
2. maximumPoolSize | int | 最大线程数。线程池允许创建的总线程数上限 |
3. keepAliveTime | long | 非核心线程空闲时的存活时间 |
4. unit | TimeUnit | keepAliveTime 的时间单位(如 SECONDS、MILLISECONDS) |
5. workQueue | BlockingQueue<Runnable> | 任务队列,用于存放待执行的任务 |
6. threadFactory | ThreadFactory | 创建新线程的工厂,可用于自定义线程名、优先级等 |
7. handler | RejectedExecutionHandler | 拒绝策略,当任务无法提交时的处理方式 |
重点:任务队列(workQueue)类型
队列类型 | 特点 | 适用场景 |
---|---|---|
ArrayBlockingQueue | 有界队列,需指定容量 | 防止资源耗尽,推荐使用 |
LinkedBlockingQueue | 无界队列(默认 | 不推荐,可能导致OOM |
SynchronousQueue | 不存储元素,每个插入必须等待取出 | 适合高并发短任务(如 |
PriorityBlockingQueue | 支持优先级排序的无界队列 | 任务有优先级需求 |
重点:拒绝策略(RejectedExecutionHandler)
当线程池已满且队列已满时,新任务将被拒绝:
策略 | 行为 |
---|---|
AbortPolicy (默认) | 抛出 |
CallerRunsPolicy | 由提交任务的线程自己执行该任务(防止任务丢失,但降低吞吐量) |
DiscardPolicy | 静默丢弃任务,不抛异常 |
DiscardOldestPolicy | 丢弃队列中最老的任务,然后重试提交 |
✅ 推荐生产环境使用
CallerRunsPolicy
或自定义拒绝策略(如写入MQ重试)。(我们的系统就是采用的这个拒绝策略)
三、线程池的底层工作原理(执行流程)
线程池的工作机制可以用一个流程图来清晰描述:
详细执行步骤:
- 提交任务
:调用
execute()
或submit()
方法 - 判断核心线程是否空闲
-
如果当前运行线程数 <
corePoolSize
,立即创建新线程执行任务
-
- 核心线程已满,尝试入队
-
如果线程数 ≥
corePoolSize
,尝试将任务放入workQueue
-
入队成功 → 等待空闲线程处理
-
- 队列已满,尝试扩容
-
如果队列已满,且当前线程数 <
maximumPoolSize
,创建非核心线程执行任务
-
- 线程池饱和,执行拒绝策略
-
如果线程数已达
maximumPoolSize
且队列满,执行RejectedExecutionHandler
-
线程回收机制
- 核心线程
:默认永不回收,除非设置了
allowCoreThreadTimeOut(true)
- 非核心线程
:空闲时间超过
keepAliveTime
后会被回收
四、线程池的最佳实践(资深工程师建议)
1. 合理设置核心参数(这些参数结合自己项目说一下,加分项)
corePoolSize
:根据CPU密集型或IO密集型任务设置
-
CPU密集型:
N
(N = CPU核数) -
IO密集型:
2N ~ 4N(我们系统是采用的2n设置的)
-
maximumPoolSize
:避免设置过大,防止系统资源耗尽
workQueue
:必须使用有界队列(如
ArrayBlockingQueue
)keepAliveTime
:建议设置为
60s
左右
2. 自定义线程工厂(便于排查问题)
ThreadFactory factory = new ThreadFactory() {
private AtomicInteger id = new AtomicInteger(1);
public Thread newThread(Runnable r){Thread t = new Thread(r);t.setName("my-pool-thread-" + id.getAndIncrement());t.setDaemon(false); // 非守护线程
return t;}
};
3. 监控线程池状态
ThreadPoolExecutor executor = (ThreadPoolExecutor) pool;
System.out.println("核心线程数: " + executor.getCorePoolSize());
System.out.println("当前线程数: " + executor.getPoolSize());
System.out.println("队列任务数: " + executor.getQueue().size());
System.out.println("已完成任务数: " + executor.getCompletedTaskCount());
4. 优雅关闭线程池
executor.shutdown(); // 不再接受新任务,等待已提交任务完成
// 或
executor.shutdownNow(); // 尝试中断所有任务// 等待关闭
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow(); // 强制关闭}
} catch (InterruptedException e) {executor.shutdownNow();Thread.currentThread().interrupt();
}
五、总结
内容 | 关键点 |
---|---|
创建方式 | 推荐使用 |
核心参数 | corePoolSize , |
工作原理 | 核心线程 → 队列 → 非核心线程 → 拒绝策略 |
最佳实践 | 有界队列 + 合理线程数 + 自定义线程工厂 + 监控 + 优雅关闭 |
💡 作为资深Java工程师的忠告:
线程池不是“越多越好”,要结合业务场景压测调优
生产环境必须监控线程池的活跃度、队列积压情况
所有异步任务都要考虑异常处理和资源释放
如果你正在设计一个高并发系统,建议将线程池配置通过配置中心(如Nacos)动态管理,便于线上调优。
希望这份深度解析能帮你彻底掌握Java线程池!
往期精选:
场景题:redis挂了怎么办?怎么保证redis不挂呢?(面试官觉得redis就是很弱鸡,动不动就挂!)
场景题:(下)redis挂了怎么办?(这次必须挂了,然后说一下解决方案)上一篇是如何保证redis不挂,这一篇是真的挂了如何处理
2025最新JAVA场景题汇总!感谢粉丝的支持!希望大家2025都有一个好工作!
场景题:mysql有100w条数据,但是redis只能保存20w条,如何保证redis的数据都是热点数据?
原创不易,你的点赞和转发是对码农的最大鼓励!