--- tags: [test/review, java, thread-pool, ThreadPoolExecutor, rejection-policy, task-queue] 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 某微服务使用了如下线程池配置进行异步任务处理: ```java ExecutorService pool = Executors.newFixedThreadPool(20); for (Request req : batchRequests) { pool.submit(() -> process(req)); // 每个请求耗时约 200ms } ``` 大促期间突然出现以下现象: - 服务响应变慢,RT 飙升 - 监控显示 JVM 堆内存持续上升并最终 OOM - CPU 使用率正常,说明不是计算瓶颈 请分析: 1. 这段代码存在什么根本性问题?为什么会触发 OOM? 2. 如果要改造这段代码,至少需要改进哪三个方面?(结合线程池配置原则) 3. 如果决定将拒绝策略改为 `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:**参考答案要点**: 1. 根本问题:`Executors.newFixedThreadPool(20)` 底层使用无界队列 `LinkedBlockingQueue`(capacity = MAX_VALUE),150 个任务全部入队排队,永远不会创建超过 20 个线程。但由于 `process(req)` 耗时 200ms,队列中堆积的任务引用不会被释放,加上请求对象本身的内存占用,堆持续增长最终 OOM。本质上是一个典型的"无界队列导致的任务堆积 OOM"。 2. ① 换为有界队列的 `ThreadPoolExecutor`(如 `ArrayBlockingQueue(100)`);② 根据 IO 密集型公式重新计算核心线程数和最大线程数;③ 显式配置拒绝策略(如 `CallerRunsPolicy`)而非依赖默认的 AbortPolicy 抛异常打断链路。 3. `CallerRunsPolicy` 会将任务回退到提交线程执行——在这个场景下,如果提交线程是 HTTP 请求线程,那么处理本身就需要 200ms 的任务会在请求线程里同步执行,直接拖慢整个业务链路,导致更多的请求涌入时雪崩效应加剧。使用时务必确认调用方的线程性质。 **评分标准**:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。 ## 关联笔记 - [[01.Java/concurrent/锁升级与 CAS 机制]]