应手动构建 ThreadPoolExecutor,显式设置核心参数:使用 ArrayBlockingQueue 限定队列容量(如1024),拒绝策略选 CallerRunsPolicy,自定义命名线程工厂,并合理调用 shutdown() 与 awaitTermination() 完成优雅关闭。
直接调用 ThreadPoolExecutor 构造函数容易漏掉关键参数组合,比如拒绝策略、队列容量、线程工厂——这些一旦设错,线上可能突然拒绝任务或 OOM。JDK 提供的 Executors 工具类封装了常见模式,但其中 Executors.newFixedThreadPool() 和 Executors.newCachedThreadPool() 有隐患:newFixedThreadPool() 用的是无界 LinkedBlockingQueue,任务堆积会吃光堆内存;newCachedThreadPool() 允许创建无限线程,突发流量下可能耗尽 OS 线程资源。
推荐手动构建 ThreadPoolExecutor,显式控制所有核心参数,并替换默认的无界队列和拒绝策略:
ArrayBlockingQueue 替代 LinkedBlockingQueue
ThreadPoolExecutor.CallerRunsPolicy(让调用线程自己执行任务,自然限流)或自定义策略记录日志+告警threadFactory,用 ThreadFactoryBuilder(Guava)或手动实现,给线程命名,便于排查allowCoreThreadTimeOut(true)(可选),让空闲核心线程也能回收ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // corePoolSize
8, // maxPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1024),
new ThreadFactoryBuilder().setNameFormat("biz-task-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
execute(Runnable) 只接受无返回值任务,失败时异常会直接抛到线程的 UncaughtExceptionHandler,若未设置,就默默吞掉;submit() 返回 Future,能捕获执行异常(调用 get() 时抛出 ExecutionException 包裹原始异常)。实际业务中:
execute() 更轻量submit() 并显式处理 Future.get(3, TimeUnit.SECONDS)
Future 的返回值——不调 get() 就无法感知任务是否失败简单调 shutdown() 不等于立刻停掉所有线程:它只是停止接收新任务,已提交任务仍会执行完。真正关闭需两步:
shutdown(),停止接收awaitTermination(long, TimeUnit) 等待完成,超时后调 shutdownNow() 尝试中断正在运行的任务(注意:仅对阻塞在 sleep/wait/join 或可响应中断的 I/O 的线程有效)awaitTermination() 返回值,为 false 说明有任务没结束,这时要考虑是否强制终止或记录告警Spring 环境下建议用 @PreDestroy 方法触发关闭逻辑,避免 JVM 退出时线程池残留。
Future 异常 = 故障静默;忘记 awaitTermination = 应用假死。