Java中自定义ThreadPoolExecutor需围绕任务类型、并发量、资源限制权衡corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler七大参数;CPU密集型设core≈CPU核数+1,IO密集型core≈2×CPU核数,maximum适当放大;推荐LinkedBlockingQueue配CallerRunsPolicy,须自定义ThreadFactory并优雅关闭。

Java 中使用 ThreadPoolExecutor 自定义线程池,关键在于合理设置核心参数,并根据业务场景选择或实现拒绝策略。参数设得不合理容易导致资源耗尽或任务堆积,拒绝策略选错可能让关键任务无声丢失。
核心参数的含义与设置建议
ThreadPoolExecutor 构造方法有 7 个参数,最常用的是以下 5 个(其余可设为 null 使用默认):
-
corePoolSize:线程池中常驻的最小线程数。即使空闲也不会被回收(除非设置了
allowCoreThreadTimeOut(true))。适合设为 CPU 核心数 + 少量 IO 等待线程,比如 CPU 密集型任务设为Runtime.getRuntime().availableProcessors(),IO 密集型可适当放大(如 ×1.5~2)。 - maximumPoolSize:线程池允许创建的最大线程数。当任务队列满且当前线程数 LinkedBlockingQueue),这个值实际不会生效。
-
keepAliveTime 和 unit:非核心线程空闲后等待多久被回收。建议设为 60 秒左右,避免频繁创建销毁开销;单位用
TimeUnit.SECONDS更直观。 -
workQueue:任务缓存队列。选型直接影响行为:
-
ArrayBlockingQueue:有界队列,配合maximumPoolSize能控住总资源,推荐用于高稳定性要求场景。 -
LinkedBlockingQueue:默认无界(容量 Integer.MAX_VALUE),易导致 OOM,仅适合任务提交速率可控、内存充足的情况。 -
SynchronousQueue:不存储任务,每个任务必须立刻由空闲线程处理,否则触发拒绝策略,适合追求低延迟、高吞吐的场景(类似“直接移交”模式)。
-
四种内置拒绝策略的实际效果
当线程数已达 maximumPoolSize 且队列已满时,新任务会触发拒绝策略。JDK 提供了 4 种实现:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
AbortPolicy(默认):直接抛出
RejectedExecutionException。适合开发测试环境,能快速暴露配置问题。 - CallerRunsPolicy:由提交任务的线程(即主线程)自己执行该任务。会降低提交速度,缓解压力,但可能拖慢上游逻辑,适合任务可短暂同步执行的场景。
- DiscardPolicy:静默丢弃任务,不报错也不通知。慎用,除非确认丢弃安全(如日志上报类低优先级任务)。
- DiscardOldestPolicy:丢弃队列头部的任务(即最早入队但还没执行的那个),再尝试提交当前任务。适合任务有时效性、新任务比旧任务更重要的情况(如实时行情更新)。
自定义拒绝策略的写法
如果内置策略都不满足需求,可以实现 RejectedExecutionHandler 接口:
立即学习“Java免费学习笔记(深入)”;
public class LogAndNotifyPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 记录日志
System.err.println("Task " + r + " rejected at " + new Date());
// 可选:发告警、落库、重试队列等
if (r instanceof Future<?>) {
((Future<?>) r).cancel(true);
}
}
}
使用时传入构造器:new ThreadPoolExecutor(..., new LogAndNotifyPolicy())。注意避免在拒绝逻辑里做耗时操作(如远程调用),否则可能阻塞提交线程。
几个容易踩的坑
- 用
Executors.newFixedThreadPool(10)看似方便,但底层用了无界队列,一旦任务积压就内存溢出——应手动构建ThreadPoolExecutor并指定有界队列。 - 把
corePoolSize设为 0,又用了SynchronousQueue,会导致没有预热线程,所有任务都走拒绝流程,除非有空闲线程及时接手。 - 拒绝策略中调用
executor.getQueue().size()或executor.getActiveCount()获取状态,这些不是原子操作,仅作参考,不能用于关键判断逻辑。 - 线程池未正确 shutdown:忘记调用
shutdown()或shutdownNow(),可能导致 JVM 无法退出。

















