cachedthreadpool空闲60秒后自动回收线程,因其底层使用synchronousqueue和60秒keepalivetime;测试时提交70秒任务,65秒后pool.getpoolsize()返回0可验证该行为。

<p>可以通过创建一个 <strong>CachedThreadPool</strong>,提交一个长期休眠的任务(如 sleep 70 秒),再观察线程数量变化来验证其“空闲 60 秒自动回收”的行为。关键在于:CachedThreadPool 底层使用的是 <strong>SynchronousQueue + 60 秒 keepAliveTime</strong> 的 ThreadPoolExecutor,空闲线程会在超时后被终止。</p> <h3>1. 构造可观察的测试环境</h3> <p>使用 <strong>Thread.activeCount()</strong> 或更准确的 <strong>ThreadPoolExecutor.getPoolSize()</strong> 来监控实际存活线程数。注意:不能只依赖 activeCount(),因为它统计 JVM 所有线程,应直接获取线程池内部状态。</p>
- 用 Executors.newCachedThreadPool() 创建线程池(等价于 new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()))
- 提交一个任务:先打印当前线程名和时间,sleep 70 秒,再打印结束
- 在任务提交后、sleep 前,立即调用 pool.getPoolSize() 查看初始线程数(应为 1)
- 主线程 sleep 5 秒后再次检查 pool.getPoolSize() —— 此时仍为 1(线程正在运行)
- 主线程再 sleep 65 秒后检查 —— 此时应降为 0(线程已因空闲超时被回收)
2. 完整可运行的验证代码
以下代码直接输出关键时间节点的线程池大小,无需外部工具即可证明回收行为:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
import java.util.concurrent.*;
public class CachedThreadPoolRecycleDemo {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor pool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
System.out.println("初始线程数: " + pool.getPoolSize());
// 提交一个 70 秒任务(模拟执行完后空闲)
pool.submit(() -> {
System.out.println("【任务开始】线程: " + Thread.currentThread().getName()
+ ", 时间: " + System.currentTimeMillis());
try {
Thread.sleep(70_000); // 超过 60 秒 keepAliveTime
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("【任务结束】线程: " + Thread.currentThread().getName());
});
// 等待线程启动(通常瞬间完成)
Thread.sleep(1000);
System.out.println("任务启动后 1 秒,线程数: " + pool.getPoolSize()); // ≈1
// 等待 5 秒 → 仍处于“运行中”,未空闲,线程不会回收
Thread.sleep(5000);
System.out.println("运行中 5 秒后,线程数: " + pool.getPoolSize()); // 仍为 1
// 再等 65 秒 → 任务已结束,线程进入空闲;60 秒超时后被回收(JVM 调度略有延迟,65 秒足够)
Thread.sleep(65_000);
System.out.println("空闲 65 秒后,线程数: " + pool.getPoolSize()); // 输出 0
pool.shutdown();
}
}
3. 关键细节说明
该行为不是“销毁工作线程”,而是回收空闲线程。CachedThreadPool 允许核心线程数为 0,所以只要线程空闲满 60 秒,就会被 terminate(从 worker 集合中移除并 stop)。
- 线程是否空闲,取决于它是否从 workQueue.poll(60, SECONDS) 中成功取到新任务;取不到即进入超时等待,超时后退出循环并终止自身
- 由于用的是 SynchronousQueue(无容量),没有排队任务,所以一旦任务执行完毕,线程立刻进入空闲等待状态
- 如果期间有新任务提交,SynchronousQueue 会立即将任务转给空闲线程,避免其超时——这正是“缓存”语义的体现
4. 补充验证技巧(增强说服力)
若想更直观看到线程生命周期,可在任务中添加 Thread.setUncaughtExceptionHandler 或使用 ThreadMXBean 监控线程创建/销毁事件,但对基础验证非必需。
- 加日志:在任务前后打印 Thread.currentThread().getId(),确认是同一个线程实例(说明没新建)
- 对比实验:把 sleep 改成 50 秒 → 最后一次检查仍为 1(未超时,未回收)
- 强制触发:调用 pool.allowCoreThreadTimeOut(true) 对其他类型池生效,但对 CachedThreadPool 本就默认允许(corePoolSize=0)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










