先看大厂规范为什么禁止 Executors
阿里开发规范明令:线程池必须手动 new ThreadPoolExecutor,禁止用 Executors 工厂方法。原因就两个坑:newFixedThreadPool 的队列是无界 LinkedBlockingQueue——任务只进不出,内存涨到 OOM;newCachedThreadPool 的 maximumPoolSize 是 Integer.MAX_VALUE——来多少任务开多少线程,线程涨到 OOM。一个炸堆内存,一个炸线程数,都不是玄学,是边界参数没守住。
手动创建:七大参数逐个点名
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // corePoolSize:常驻核心线程数
8, // maximumPoolSize:线程数天花板
60, TimeUnit.SECONDS, // keepAliveTime:闲线程存活时间
new ArrayBlockingQueue<>(100), // workQueue:排队的队列
new NamedThreadFactory("order-pool"), // threadFactory:线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // handler:拒绝策略
);前四个管「人」:核心线程是常驻编制;忙不过来时先扩到天花板;闲下来按 keepAliveTime 裁掉多余线程(核心线程默认不裁,allowCoreThreadTimeOut 可改)。后三个管「事」:队列排队、工厂造线程、拒绝策略兜底。线程工厂最容易被忽视却最影响排障——不给线程起名,jstack 里全是 pool-1-thread-N,出事时谁是谁根本分不清。
一个任务的完整旅程
提交一个任务后,线程池按固定顺序判定,很多人记反,务必记牢:
提交任务
→ 核心线程未满? 建核心线程执行
→ 队列没满? 入队排队
→ 线程数未到天花板? 建非核心线程救急
→ 都满了 触发拒绝策略注意顺序:队列先于非核心线程——线程池宁可让任务排队,也不轻易扩编制。这解释了一个反直觉现象:配无界队列时,maximumPoolSize 和 keepAliveTime 永远不生效,因为队列永远不满,非核心线程永远轮不到创建。参数之间是联动的,不是各管各的。
submit 的陷阱:吞异常
submit 比 execute 多包一层 Future:能拿结果,但任务抛的异常也藏进了 Future 里——不主动 get 就永远没人知道,日志和监控统统看不见。多少「线程池任务静默失败」的事故,根子都在这。生产代码要么统一处理 Future 的异常,要么干脆用 execute 配合 ThreadFactory 里设置 UncaughtExceptionHandler。
参数怎么定才不是拍脑袋?四种拒绝策略各适合什么?怎么不重启就调参?下一篇全部给出答案。
评论 (0)