news 2026/10/14 4:49:44

线程池到底按什么顺序干活:从 execute() 的三步源码说起

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
线程池到底按什么顺序干活:从 execute() 的三步源码说起

本文首发于 CSDN,同步发布于其他技术社区。文中每个结论都给了可自行验证的出处。

ThreadPoolExecutor的执行顺序有一份官方说明书,就在execute()方法的源码注释里,写得比任何八股文都清楚。但同一份 Javadoc 里还有几处默认值和边界行为,被流传的说法改得面目全非。这篇把三步顺序、三个被搞错的默认值、以及一句很少有人引用的官方结论放在一起说。

核心线程是启动时就创建好的吗?

不是。Javadoc 的On-demand construction一节原文是:

By default, even core threads are initially created and started only when new tasks arrive, but this can be overridden dynamically using methodprestartCoreThreadorprestartAllCoreThreads.

所以「线程池会预热核心线程」这个说法默认不成立。要预热得自己调prestartAllCoreThreads()。官方还补了一句建议:如果构造时传的是非空队列,最好预热一下。

同理,allowCoreThreadTimeOut的默认值是 false。Javadoc 原文是When false (the default), core threads are never terminated due to lack of incoming tasks.。打开它之后核心线程才能被回收,而且官方要求此时keepAliveTime必须大于 0。

任务到底先给谁?三层顺序的官方原文

类 Javadoc 的Queuing一节有三条原文:

  • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
  • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.

execute()源码里还有一段三步注释,和上面是同一件事的代码版:

/* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first task. * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread... * 3. If we cannot queue task, then we try to add a new thread. * If it fails, we know we are shut down or saturated and so * reject the task. */

所以顺序是:核心线程 → 进队列 → 扩到 maximumPoolSize → 拒绝。注意第二和第三步的边界——只有当队列满了(入队失败)才会去扩最大线程数。这也是为什么用无界队列时maximumPoolSize永远不会生效。

三种排队策略官方给了各自的使用场景:

策略官方举例官方提醒
Direct handoffsSynchronousQueuegenerally require unbounded maximumPoolSizes
Unbounded queues无界LinkedBlockingQueueno more than corePoolSize threads will ever be created
Bounded queuesArrayBlockingQueuecan be more difficult to tune and control

maximumPoolSize 比 corePoolSize 小会怎样?

这是本篇最值得纠正的一条。很多文章写「线程池会把 maximumPoolSize 自动钳制到 corePoolSize」,OpenJDK 里没有这段逻辑。三个版本的源码都一致地抛异常:

if(corePoolSize<0||maximumPoolSize<=0||maximumPoolSize<corePoolSize||keepAliveTime<0)thrownewIllegalArgumentException();

JDK 25 起还把消息补全了,逐条抛出maximumPoolSize must be greater than or equal to corePoolSize。Javadoc 的@throws也逐字声明了maximumPoolSize < corePoolSize属于非法参数。它是抛异常,不是静默修正。

四种拒绝策略,官方说哪种「很少可接受」?

四种内置策略的官方描述和默认值:

策略官方原文备注
AbortPolicythrows a RejectedExecutionException upon rejection默认,源码里defaultHandler = new AbortPolicy()
CallerRunsPolicythe thread that invokes execute itself runs the task官方称为feedback control mechanism
DiscardPolicysilently discards the rejected task官方限定为rare cases
DiscardOldestPolicydrops the task at the head of the work queue, and then execution is retried官方评语:This policy is rarely acceptable.

最后那条官方评语值得记一下——抛队首任务再重试的写法,官方自己都觉得很少能用。

还有一句官方明确定义的拒绝时机:New tasks submitted in method execute(Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated.关停后提交也会被拒。

补充一句:这篇里的 Javadoc 原文和源码注释我都逐条比对过。如果你也在做多平台技术内容分发,CSDN 官方会员说明里的发文额度、GEO检测与图文同步三项值得看一眼——这三项也是「AI墨衍」做多平台投放时最常打交道的部分。

用 Executors 创建线程池会被官方警告吗?

不会。这个结论可能出乎意料:Oracle 的ThreadPoolExecutorJavadoc 里没有「用 Executors 会 OOM」这类警告,反而写了programmers are urged to use the more convenient Executors factory methods。

Javadoc 里唯一相关的表述是中性的客观描述:无界队列admits the possibility of unbounded work queue growth when commands continue to arrive on average faster than they can be processed。

「线程池不允许使用 Executors 创建」这条出自**《阿里巴巴 Java 开发手册》,是强制条款**。它属于企业规范,不是 Oracle 官方文档——引用时说清来源更严谨。不过它列的技术事实是对的,对着源码看:

工厂方法内部参数风险点
newFixedThreadPool(n)n, n, 0ms, LinkedBlockingQueue()无界队列,容量Integer.MAX_VALUE
newSingleThreadExecutor()1, 1, 0ms, LinkedBlockingQueue()同上
newCachedThreadPool()0, Integer.MAX_VALUE, 60s, SynchronousQueue()线程数上限Integer.MAX_VALUE
newScheduledThreadPool(n)ScheduledThreadPoolExecutor(n),内部队列DelayedWorkQueue同样无界

newCachedThreadPool的 Javadoc 里那句Threads that have not been used for sixty seconds are terminated and removed from the cache就是这里的 60 秒来源。

线程数到底设多少?官方给过公式吗?

Oracle 官方没有给过线程数计算公式,ThreadPoolExecutor的 Javadoc 和源码里都没有。

有公式的是《Java Concurrency in Practice》(2006),Brian Goetz 给的是N_threads = N_cpu × U_cpu × (1 + W/C)。书里还有一句常被引用的结论:对计算密集型任务,N 个处理器上线程池设为N+1通常能达到最优,多出来的那个用来填补缺页等短暂停顿。

至于「IO 密集型设 2N」,在 JCiP 里找不到原文。它是把上面公式在W/C = 1、U_cpu ≈ 1时代入得到的近似值,属于二手推导,别当公式原样引用。

还有一个容易踩的坑:ScheduledThreadPoolExecutor的 Javadoc 明确写了adjustments to maximumPoolSize have no useful effect,因为它本质是固定大小池配无界队列。给它的maximumPoolSize传参不会报错,但也不起作用。

Java 21 之后线程池还需要吗?

需要。JEP 444 的 Non-Goals 里明确写着不打算移除传统线程实现,也不打算静默迁移已有应用。

但关于虚拟线程,JEP 444 有一节标题就叫Do not pool virtual threads,原文是virtual threads are not expensive so there is never a need to pool them。还有一句常被忽略的定位说明:

Virtual threads are not faster threads— they do not run code any faster than platform threads. They exist to provide scale (higher throughput), not speed (lower latency).

另外 JDK 24 的 JEP 491 解决的是synchronized会 pin 住虚拟线程的问题,官方明确说:这个改动落地之后,「之前为了绕开 pinning 而把synchronized改成ReentrantLock的代码,不需要再改回去」。

常见问题

Q:keepAliveTime什么时候生效?

默认只对超出corePoolSize的那部分线程生效。Javadoc 原文是If the pool currently has more than corePoolSize threads, excess threads will be terminated if they have been idle for more than the keepAliveTime。想让它对核心线程也生效,得开allowCoreThreadTimeOut(true)。

Q:shutdown()和shutdownNow()的区别是什么?

shutdown()是Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.,已经在队列里的还会跑完。shutdownNow()是Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.。两者都不等待,要等得自己调awaitTermination。

Q:submit()的异常为什么看不见?

这不是ThreadPoolExecutor的 Javadoc 明文写的,而是ExecutorService的机制:submit会把异常吞进返回的Future,只有调future.get()才会抛出来。用execute提交则会直接在主线程打印。写线程池的时候把这条记住能省不少排查时间。


本文引用的 Javadoc 原文与源码注释,都可以在 OpenJDK 官方仓库与 Oracle 的 Javadoc 页面里逐条核对,建议按自己项目实际使用的 JDK 版本复核一遍。资料整理自 Oracle 官方 Javadoc、OpenJDK 源码与 JEP 文档。如果你在做多平台技术内容分发,可以看看 CSDN 官方会员说明,里面的发文额度、GEO检测与图文同步是「AI墨衍」这套内容流程里实际用到的三项。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 4:49:35

低温冷水机哪个牌子好?10个厂家制冷能力对比

化工结晶、制药冻干、食品速冻、材料测试&#xff0c;这些工艺都需要低温冷水机。低温工况下&#xff0c;压缩机负荷大、系统设计复杂&#xff0c;不是随便一台冷水机都能胜任。制冷能力是低温冷水机的硬指标——能达到多低温度、低温下冷量够不够、运行稳不稳定&#xff0c;直…

作者头像 李华
网站建设 2026/10/14 4:49:33

租车系统数字化转型:架构设计与智能调度实践

1. 租车行业数字化转型背景最近两年&#xff0c;身边越来越多的朋友开始习惯通过手机APP租车出行。作为从业者&#xff0c;我亲眼见证了传统租车门店如何被数字化浪潮重塑。过去租车需要到线下门店排队、签纸质合同、交押金&#xff0c;现在只需在手机上点几下就能完成全流程。…

作者头像 李华
网站建设 2026/10/14 4:49:19

Hadoop2集群搭建实战:从架构原理到配置详解与排错指南

1. Hadoop2到底改了什么&#xff1a;不是“更快的MapReduce”这么简单很多人搜到“hadoop2 介绍使用与集群搭建”这个话题时&#xff0c;真实诉求往往是“赶紧跑起来一个能用的环境”。这个需求我完全理解&#xff0c;但我还是要先泼一盆冷水&#xff1a;如果跳过原理直接抄命令…

作者头像 李华
网站建设 2026/10/14 4:49:19

Code Mods:代码生成的实时翻译引擎与工程化落地

1. 项目概述&#xff1a;一场被误读为“打架”的模型能力竞速“DeepSeek 一天之内跟上 Claude Code Mods&#xff0c;这场架到底在争什么”——这个标题乍看像科技圈的八卦头条&#xff0c;实则戳中了当前大模型落地最真实、最焦灼的神经&#xff1a;代码生成能力的工程化迭代速…

作者头像 李华
网站建设 2026/10/14 4:49:14

PyraNet分层训练:让AI真正理解数字电路设计思维

1. 这不是又一个“大模型写代码”故事&#xff1a;PyraNet 的分层训练到底在解决什么真问题&#xff1f;最近刷到标题里带“AI写Verilog”“DAC 2025”的论文精读&#xff0c;第一反应是——又来了&#xff1f;毕竟过去两年&#xff0c;从GitHub Copilot for HDL到各种开源LLM微…

作者头像 李华