threadpool
线程池 ThreadPoolExecutor
ToC
- 核心参数与工作流程
- 任务提交流程与状态机
- 队列选择与拒绝策略
- 线程数配置与调优
- 常见坑与最佳实践
核心参数与工作流程
ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler):
处理顺序(execute):
- 运行线程 < core → 直接新建工作线程执行任务;
- 否则入队
workQueue; - 队列满且运行线程 < max → 新建线程;
- 否则触发拒绝策略。
其他:
allowCoreThreadTimeOut(true):核心线程也会因空闲超时而回收;prestartAllCoreThreads():预热核心线程。
任务提交流程与状态机
内部用一个整型 ctl 打包线程数与运行状态:RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED。
execute(Runnable):抛异常式提交;submit(Callable):返回Future,异常包装在ExecutionException中;- 关闭:
shutdown()(有序,处理队列剩余任务) vsshutdownNow()(中断工作线程并返回剩余任务)。
队列选择与拒绝策略
队列:
- 无界队列
LinkedBlockingQueue:易导致仅用到 core 线程,队列堆积; - 有界队列
ArrayBlockingQueue:更可控,建议结合拒绝策略; - 直通队列
SynchronousQueue:常与max>core配合用于突发流量(如CachedThreadPool)。
拒绝策略(RejectedExecutionHandler):
AbortPolicy(默认,抛 异常)、CallerRunsPolicy(调用方执行)、DiscardPolicy、DiscardOldestPolicy;推荐业务自定义埋点与降级。
线程数配置与调优
经验公式(粗略):
- CPU 密集:
core = CPU核数 + 1; - I/O 密集:
core ≈ CPU核数 * (1 + 阻塞时间/计算时间); - 以响应时间为目标可测
队列长度、任务处理时延、拒绝率来回调参数。
示例:
int cores = Runtime.getRuntime().availableProcessors();
BlockingQueue<Runnable> q = new ArrayBlockingQueue<>(1000);
ThreadFactory tf = r -> { Thread t = new Thread(r); t.setName("biz-pool-"+t.getId()); return t; };
RejectedExecutionHandler h = new ThreadPoolExecutor.CallerRunsPolicy();
ThreadPoolExecutor pool = new ThreadPoolExecutor(cores, cores*2, 60, TimeUnit.SECONDS, q, tf, h);
pool.allowCoreThreadTimeOut(true);
常见坑与最佳实践
- 不要使用
Executors.newFixedThreadPool/newCachedThreadPool默认配置(无界队列/最大线程等风险),优先显式ThreadPoolExecutor。 - 线程命名与
UncaughtExceptionHandler便于排查;submit的异常需要显式future.get()才能暴露。 - 结合
Metrics/JFR观测:队列长度、活动线程、任务耗时、拒绝次数。 - 防止任务内阻塞外部资源(DB/缓存/HTTP)拖垮线程池,设置超时与隔离(如 Bulkhead)。