
1) 【一句话结论】
根据任务特性(CPU密集/IO密集/可缓存)和系统负载,动态配置线程池参数(核心线程数、最大线程数、存活时间、队列类型),平衡资源占用与任务响应,结合拒绝策略确保业务连续性,避免资源浪费或任务积压。
2) 【原理/概念讲解】
线程池的三个核心参数决定其行为:
3) 【对比与适用场景】
| 任务类型 | corePoolSize | maximumPoolSize | 队列类型 | 适用场景 | 注意点 |
|---|---|---|---|---|---|
| CPU密集型 | CPU核心数 | 核心数或略大 | 有界队列(如ArrayBlockingQueue) | 长计算任务(如排序、复杂计算) | 队列大小需匹配任务量(如队列容量=请求速率×任务执行时间),避免溢出 |
| IO密集型 | 较小(5-10) | 较大(20-50) | 无界队列(如LinkedBlockingQueue)或SynchronousQueue | 短任务(如网络请求、数据库查询) | 空闲线程被回收,减少资源占用;队列大小需根据请求速率计算(如请求速率1000req/s,任务耗时1s,队列容量=1000) |
| 任务可缓存型 | 0 | Integer.MAX_VALUE | 无界队列或SynchronousQueue | 网络请求、轻量级任务(可重用) | 需设置keepAliveTime避免内存泄漏(如60秒),任务无长期执行需求 |
4) 【示例】
假设快手处理高并发HTTP请求(IO密集型),配置线程池:
// IO密集型线程池配置(模拟快手业务场景)
ExecutorService ioThreadPool = new ThreadPoolExecutor(
5, // corePoolSize: 5个核心线程,处理高频IO请求
20, // maximumPoolSize: 20个最大线程,应对突发请求
30, // keepAliveTime: 30秒,空闲线程超过30秒回收
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), // 队列大小1000,暂存请求
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用者执行任务,避免任务丢失
);
ioThreadPool.submit(() -> {
// 模拟网络IO(如调用第三方API)
try {
Thread.sleep(1000); // 模拟IO耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("请求处理完成");
});
解释:核心线程5个处理高频IO请求,最大线程20应对突发,队列1000暂存请求,30秒回收空闲线程,拒绝策略用CallerRuns避免任务中断。队列大小根据请求速率(假设1000req/s,任务耗时1s)计算,确保突发请求能暂存,避免溢出。
5) 【面试口播版答案】
面试官您好,关于线程池参数选择,核心是根据任务特性和系统负载动态匹配。比如处理高并发的IO密集型任务(如用户HTTP请求),我会设置corePoolSize为5(核心线程数),因为这类任务IO耗时远大于计算,空闲时线程会被回收;maximumPoolSize设为20,应对突发请求;keepAliveTime设为30秒,空闲线程超过30秒回收,避免资源浪费。队列用LinkedBlockingQueue,暂存1000个请求,防止队列溢出。参数影响方面,corePoolSize决定长期资源占用,最大线程数限制并发,keepAliveTime控制空闲线程回收,拒绝策略选择CallerRuns避免任务丢失。这样既能减少资源占用,又能快速响应请求,提升系统性能。如果系统负载变化,比如CPU利用率低于70%时,我会减少corePoolSize(比如从5降到4),高于90%时增加(比如增加到8),通过监控CPU使用率和任务队列长度动态调整,确保资源利用率最优。
6) 【追问清单】
7) 【常见坑/雷区】