8.3 KiB
tags, create time
| tags | create time | ||||||
|---|---|---|---|---|---|---|---|
|
2026-08-09 12:00 |
线程池参数与拒绝策略_测试题
概述
本测试覆盖 ThreadPoolExecutor 七大参数、任务提交流程、四种拒绝策略、工作队列类型以及 CPU/IO 密集型调优公式,共 10 道题目(6 道选择题 + 3 道填空题 + 1 道简答题)。
一、选择题(6道,由浅入深)
难度阶梯: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
Q1(基础)— 考察定义层面
ThreadPoolExecutor 构造方法的七个参数中,前五个决定了线程池的什么?
A. 线程的安全性和死锁检测
B. 容量模型和任务调度逻辑
C. 线程的名称格式和优先级
D. 拒绝策略的具体行为
Q2(基础)— 考察行为判断
调用 Executors.newFixedThreadPool(10) 时,底层实际使用的是哪种队列?
A. ArrayBlockingQueue(有界)
B. LinkedBlockingQueue(无界,capacity = Integer.MAX_VALUE)
C. SynchronousQueue
D. PriorityBlockingQueue
Q3(进阶)— 考察核心原理
当一个线程池配置了 corePoolSize=8、maximumPoolSize=16、workQueue 容量为 100 时,假设当前已有 8 个核心线程在运行,此时同时提交 150 个新任务。请问有多少任务会被放入队列、多少会触发非核心线程创建、多少会触发拒绝策略?
A. 100 入队 + 0 创建非核心 + 50 触发拒绝
B. 100 入队 + 8 创建非核心 + 42 触发拒绝
C. 100 入队 + 16 创建非核心 + 34 触发拒绝
D. 150 全部入队,不会触发非核心线程
Q4(进阶)— 考察比较与辨析
关于四种拒绝策略,以下描述正确的是:
A. AbortPolicy 是默认策略,会静默丢弃任务而不抛出异常
B. CallerRunsPolicy 会将任务回退到提交任务的线程执行,起到降速缓冲的作用
C. DiscardOldestPolicy 是最安全的策略,永远不会丢失任何任务
D. DiscardPolicy 适合处理可以丢失的非关键任务,会抛出异常通知调用方
Q5(深入)— 考察场景推理
某服务在流量突增时出现了 OOM 错误,经排查发现使用的是 Executors.newCachedThreadPool()。最可能的原因是:
A. CachedThreadPool 的工作队列是有界的,流量大会导致拒绝策略触发
B. CachedThreadPool 的核心线程数为 0,所有任务都必须创建新线程,无限增长导致 OOM
C. CachedThreadPool 的 keepAliveTime 太短,频繁创建销毁线程带来额外开销
D. CachedThreadPool 使用 ArrayBlockingQueue 且有容量上限
Q6(深入)— 考察数值计算
在一台 8 核机器上运行 IO 密集型任务(阻塞系数取 0.85),根据经验公式,推荐的线程数约为:
A. 9 个(N + 1)
B. 16 个(2 × N)
C. 53 个(N / (1 - 0.85))
D. 128 个(N × 16)
二、填空题(3道)
F1 — 填空1
线程池任务提交的流转逻辑遵循以下优先级:第一步创建核心线程;第二步如果已达核心线程数且队列未满则__(1);第三步如果队列已满但线程数未达到 maximumPoolSize 则创建(2);第四步如果线程数达到上限且队列仍满则触发(3)__。
提示: 回忆流程图中的四个分支路径,注意每一步的前置条件。
F2 — 填空2
在 8 核机器上处理 CPU 密集型任务,推荐的线程数公式是 (1)(其中 N 为 CPU 核心数),多出来的那一个线程是为了容忍页缺失等偶发停顿。而对于 IO 密集型任务(阻塞系数 0.85),公式则是 (2)。
提示: CPU 密集型和 IO 密型的公式完全不同,前者简单后者需要引入阻塞系数。
F3 — 填空3
ArrayBlockingQueue 属于__(1)类型的阻塞队列,特点是 FIFO 且可控,(2)__在生产中使用。与之相对,LinkedBlockingQueue 的默认 capacity 为 (3),容易导致任务堆积甚至 OOM。
提示: 关注各队列类型的"有界/无界"属性和生产推荐度。
三、简答题(1道)
S1
某微服务使用了如下线程池配置进行异步任务处理:
ExecutorService pool = Executors.newFixedThreadPool(20);
for (Request req : batchRequests) {
pool.submit(() -> process(req)); // 每个请求耗时约 200ms
}
大促期间突然出现以下现象:
- 服务响应变慢,RT 飙升
- 监控显示 JVM 堆内存持续上升并最终 OOM
- CPU 使用率正常,说明不是计算瓶颈
请分析:
- 这段代码存在什么根本性问题?为什么会触发 OOM?
- 如果要改造这段代码,至少需要改进哪三个方面?(结合线程池配置原则)
- 如果决定将拒绝策略改为
CallerRunsPolicy,需要注意什么风险?
答题框架提示:
- 第 1 问抓住 Executors.newFixedThreadPool 的底层队列特性
- 第 2 问从"有界队列 + 自定义核心线程数 + 手动构造"入手
- 第 3 问从 CallerRunsPolicy 的行为特征推导其对业务链路的影响
参考答案与解析
选择题答案
| 题号 | 正确答案 | 解析 |
|---|---|---|
| Q1 | B | 前五个参数(corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue)共同定义了线程池的容量模型和任务调度逻辑。threadFactory 和 handler 是扩展点。 |
| Q2 | B | newFixedThreadPool 内部使用 LinkedBlockingQueue,其默认 capacity 为 Integer.MAX_VALUE(无界),这会导致任务不断堆积而不会触发非核心线程创建或拒绝策略。 |
| Q3 | B | 核心线程 8 个已满,150 个任务先到队列排,队列容量 100 入队 100 个;剩余 50 个触发第三步创建非核心线程,最多可创建 16-8=8 个非核心线程处理 8 个任务;最后 50-8=42 个触发拒绝策略。 |
| Q4 | B | B 正确:CallerRunsPolicy 由提交线程执行,起到降速缓冲作用。A 错(AbortPolicy 抛出异常),C 错(DiscardOldestPolicy 会丢弃老任务),D 错(DiscardPolicy 静默丢弃不抛异常)。 |
| Q5 | B | CachedThreadPool 的 corePoolSize=0,所有任务都需新建线程处理,且 keepAliveTime=60s。无界队列 + 无限创建线程,在高并发下线程数暴增导致 OOM。 |
| Q6 | C | IO 密集型公式:N / (1 - 阻塞系数) = 8 / (1 - 0.85) = 8 / 0.15 ≈ 53 个线程。CPU 密集型的 N+1=9 不适用于 IO 场景。 |
填空题答案
| 题号 | 答案 | 解析 |
|---|---|---|
| F1 | (1)放入队列排队 (2)非核心线程 (3)拒绝策略 | 任务提交的四级决策路径:核心线程→队列→非核心线程→拒绝策略。缺一不可。 |
| F2 | (1)N + 1 (2)N / (1 - 阻塞系数) | CPU 密集型几乎一直在计算,不需要等待 IO,多余线程只会增加上下文切换开销;IO 密集型线程大量时间在等待 IO,可以多开线程提高利用率。 |
| F3 | (1)有界 (2)推荐 (3)Integer.MAX_VALUE | ArrayBlockingQueue 明确指定容量上限,安全可控;LinkedBlockingQueue 默认无界是大坑。 |
简答题参考答案
S1:参考答案要点:
- 根本问题:
Executors.newFixedThreadPool(20)底层使用无界队列LinkedBlockingQueue(capacity = MAX_VALUE),150 个任务全部入队排队,永远不会创建超过 20 个线程。但由于process(req)耗时 200ms,队列中堆积的任务引用不会被释放,加上请求对象本身的内存占用,堆持续增长最终 OOM。本质上是一个典型的"无界队列导致的任务堆积 OOM"。 - ① 换为有界队列的
ThreadPoolExecutor(如ArrayBlockingQueue(100));② 根据 IO 密集型公式重新计算核心线程数和最大线程数;③ 显式配置拒绝策略(如CallerRunsPolicy)而非依赖默认的 AbortPolicy 抛异常打断链路。 CallerRunsPolicy会将任务回退到提交线程执行——在这个场景下,如果提交线程是 HTTP 请求线程,那么处理本身就需要 200ms 的任务会在请求线程里同步执行,直接拖慢整个业务链路,导致更多的请求涌入时雪崩效应加剧。使用时务必确认调用方的线程性质。
评分标准:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。