应手动构建 ThreadPoolExecutor,显式设置核心参数:使用 ArrayBlockingQueue 限定队列容量(如1024),拒绝策略选 CallerRunsPolicy,自定义命名线程工厂,并合理调用 shutdown() 与 awaitTermination() 完成优雅关闭。

为什么不能直接 new ThreadPoolExecutor?
直接调用 ThreadPoolExecutor 构造函数容易漏掉关键参数组合,比如拒绝策略、队列容量、线程工厂——这些一旦设错,线上可能突然拒绝任务或 OOM。JDK 提供的 Executors 工具类封装了常见模式,但其中 Executors.newFixedThreadPool() 和 Executors.newCachedThreadPool() 有隐患:newFixedThreadPool() 用的是无界 LinkedBlockingQueue,任务堆积会吃光堆内存;newCachedThreadPool() 允许创建无限线程,突发流量下可能耗尽 OS 线程资源。
如何安全地创建一个可监控的固定线程池?
推荐手动构建 ThreadPoolExecutor,显式控制所有核心参数,并替换默认的无界队列和拒绝策略:
- 用
ArrayBlockingQueue替代LinkedBlockingQueue,限定队列长度(如 1024) - 拒绝策略选
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()
);
submit() 和 execute() 到底该用哪个?
execute(Runnable) 只接受无返回值任务,失败时异常会直接抛到线程的 UncaughtExceptionHandler,若未设置,就默默吞掉;submit() 返回 Future,能捕获执行异常(调用 get() 时抛出 ExecutionException 包裹原始异常)。实际业务中:
- 异步发通知、写日志等“火种型”任务,用
execute()更轻量 - 需要结果、超时控制或错误重试的任务,必须用
submit()并显式处理Future.get(3, TimeUnit.SECONDS) - 永远不要忽略
Future的返回值——不调get()就无法感知任务是否失败
线程池 shutdown 的正确姿势是什么?
简单调 shutdown() 不等于立刻停掉所有线程:它只是停止接收新任务,已提交任务仍会执行完。真正关闭需两步:
一套傻瓜式的建站程序,由前台购物、后台管理、在线支付三部分组成介绍说明:1.注册与否均可购物(同类程序大多要求注册才能购物),方便了那些懒得注册的客户。降低用户使用门槛,自然可抓住更多潜在商机。2.会员等级和折扣功能。管理员可方便的为会员设置不同等级,不同等级的员会可享受不同的购物折扣。3.站内短信、留言发布,沟通无极限。会员和游客均可发送短信和留言。4.完美融合在线支付功能,无需编程、无需修改源
立即学习“Java免费学习笔记(深入)”;
- 先调
shutdown(),停止接收 - 再调
awaitTermination(long, TimeUnit)等待完成,超时后调shutdownNow()尝试中断正在运行的任务(注意:仅对阻塞在sleep/wait/join或可响应中断的 I/O 的线程有效) - 务必检查
awaitTermination()返回值,为false说明有任务没结束,这时要考虑是否强制终止或记录告警
Spring 环境下建议用 @PreDestroy 方法触发关闭逻辑,避免 JVM 退出时线程池残留。
Future 异常 = 故障静默;忘记 awaitTermination = 应用假死。









