异步工具提交成功以后,任务可能正在运行,也可能还在排队。先固定执行器容量和拒绝策略,再观察任务何时、在哪条线程上开始。
前置:工具调用与取消。适用于商户查询工具已跑通、开始配置自定义异步执行器的阶段。以下代码与门闩实验均未运行。
排队次序与提交线程
ThreadPoolTaskExecutor 的核心线程数、最大线程数和队列容量必须一起看。达到 corePoolSize 后先入队,队列满后才扩至 maxPoolSize;线程和队列都满时拒绝任务。因此无界队列不会因为 maxPoolSize 较大就自动扩容到最大值,并可能积累大量待处理工作。API 的默认配置是核心线程 1、最大线程和队列容量无限;这是该类的默认值,不等于 Spring Boot 自动配置或用户当前项目的值。
默认 AbortPolicy 对应 Spring 的 TaskRejectedException;CallerRunsPolicy 在执行器仍运行的饱和场景下让提交线程自己执行任务。工程含义是“没有抛拒绝异常”不等于“已异步交给后台”,调用线程可能被慢查询占住。不要以静默丢弃来伪装工具完成;排队限额也不是模型 token 预算、跨实例并发限额或持久化任务保证。
最小配置
以下数字只构造可控测试条件,不是生产参数建议。
var executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(1);
executor.setThreadNamePrefix("shop-test-");
executor.initialize();
// 使用 execute 提交测试任务;测试 finally 中释放门闩并 executor.shutdown()
练习一 观察拒绝与扩容
用 CountDownLatch 控制两个虚构慢查询,不依赖 sleep 猜时序。任务 A 启动后阻塞;提交 B 留在队列;提交 C 后等待 C 已启动并阻塞;此时提交 D,断言抛 TaskRejectedException 且 D 的业务计数为零。记录 A/C 已开始、B 未开始及各任务终态;finally 释放门闩。再用新执行器设置足够大的队列作有限对照,验证 B/C 入队而不是因为 max=2 立即产生第二个工作线程。不要实际制造内存耗尽。
练习二 观察 CallerRunsPolicy
新建同规格执行器并显式设置 new ThreadPoolExecutor.CallerRunsPolicy(),在 A/C 阻塞、B 排队后,由独立受控提交线程调用 execute(D)。让 D 记录线程名并通过另一门闩阻塞;断言 D 在该提交线程执行,execute 尚未返回,释放 D 后才返回。测试必须设置有界等待与 finally 清理,避免测试自身挂死;只覆盖运行中的饱和,不推论关闭后的行为。
验收范围
已有异步身份、日志上下文和工具预算的规则仍需分别验收。这里没有核验 Spring Boot 实际执行器配置、数据库连接池联动或生产容量;按项目依赖版本核对后再执行,不要求为此升级。
来源与版本
Spring Framework 7.0.10。资料原核查时间为 2026 年 10 月 27 日 00:12–00:13 JST;独立发布日期未标明。来源:Task Execution and Scheduling 的 executor 配置章节、ThreadPoolTaskExecutor API。