news 2026/2/14 23:05:19

手写 Java 线程池:从状态转换到拒绝策略的极致实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手写 Java 线程池:从状态转换到拒绝策略的极致实现

🚀 引言:为什么要手写线程池?

在日常开发中,我们习惯了Executors.newFixedThreadPool()或者直接new ThreadPoolExecutor()。但你是否思考过:

  • 为什么线程池能让线程“长生不老”而不被销毁?
  • 一个int变量是如何神奇地同时表示“线程数量”和“运行状态”的?
  • 当任务洪峰来临时,线程池内部的微秒级调度逻辑是怎样的?

今天,我们摒弃那些繁琐的参数定义,直接动刀,从零实现一个具备核心调度、任务队列、拒绝策略功能的工业级线程池。


一、 核心设计:线程池的“灵魂”状态机

线程池不是简单的线程集合,而是一个复杂的状态机。在 JDK 中,作者 Doug Lea 巧妙地使用了一个AtomicInteger(32位)来存储两个信息:

  1. 高 3 位:表示线程池运行状态(RUNNING, SHUTDOWN, STOP…)。
  2. 低 29 位:表示当前有效线程数。

1.1 状态转换逻辑流

创建并初始化

调用 shutdown()

调用 shutdownNow()

任务清空

队列 & 线程池全空

线程池全空

terminated() 执行完毕

RUNNING

SHUTDOWN

STOP

TIDYING

TERMINATED


二、 第一阶段:构建核心骨架与 Worker 模型

线程池的核心在于Worker(工作者)。它本质上是一个不断从BlockingQueue中获取任务并执行的循环体。

2.1 任务承载:Worker 内部类实现

privatefinalclassWorkerimplementsRunnable{Threadthread;RunnablefirstTask;Worker(RunnablefirstTask){this.firstTask=firstTask;this.thread=newThread(this);// 真正执行的线程}@Overridepublicvoidrun(){runWorker(this);}}

2.2 核心循环:让线程“复用”的秘密

finalvoidrunWorker(Workerw){Runnabletask=w.firstTask;w.firstTask=null;try{// 【核心代码】如果任务不为空,或者从队列中能取到任务,就一直循环while(task!=null||(task=getTask())!=null){try{task.run();// 执行真正的业务逻辑}finally{task=null;}}}finally{processWorkerExit(w);// 线程退出后的清理}}

三、 第二阶段:调度逻辑——execute方法的精妙推演

当我们调用execute(runnable)时,线程池会经历三个关键判断:

  1. 核心线程数(corePoolSize):没满?直接创线程执行。
  2. 阻塞队列(workQueue):满了?尝试放进队列。
  3. 最大线程数(maximumPoolSize):队列也满了?尝试开启非核心线程。
  4. 拒绝策略(Handler):全满了?触发拒绝。

3.1 调度流程图(Mermaid 渲染)

成功

失败

提交任务 execute

当前线程数 < corePoolSize?

addWorker: 创建核心线程

workQueue.offer: 尝试入队

等待 Worker 调度

当前线程数 < maximumPoolSize?

addWorker: 创建非核心线程

执行拒绝策略 RejectedExecutionHandler


四、 第三阶段:极致实现——自定义拒绝策略

当系统负载达到极限,如何体面地拒绝任务?我们需要提供一个接口:

publicinterfaceRejectedExecutionHandler{voidrejectedExecution(Runnabler,MyThreadPoolExecutorexecutor);}// 模拟 JDK 的 DiscardOldestPolicy(丢弃最老任务策略)publicclassDiscardOldestPolicyimplementsRejectedExecutionHandler{publicvoidrejectedExecution(Runnabler,MyThreadPoolExecutorexecutor){executor.getQueue().poll();// 弹出队首executor.execute(r);// 重新尝试提交}}

五、 隐形陷阱:手写线程池时的 3 个坑

5.1 变量可见性与原子性

在增加线程计数时,必须使用AtomicIntegercompareAndSet(CAS),否则在高并发下,线程数会超出你的限制,直接撑爆内存。

5.2 线程工厂(ThreadFactory)的重要性

不要在代码里硬编码new Thread()。手写时,一定要支持传入ThreadFactory,以便为线程命名。没有名字的线程池,排查 OOM 时就是噩梦。

5.3 锁的颗粒度

在维护HashSet<Worker>(保存存活线程的容器)时,必须加全局锁(如ReentrantLock),这决定了线程池的线程安全性。


六、 总结:从手写到架构思考

通过手写,你会发现ThreadPoolExecutor的伟大之处在于它对资源的极致克制。它利用CAS + 阻塞队列,在无锁化和稳定性之间取得了近乎完美的平衡。

架构师建议:生产环境严禁使用Executors的快捷方法,一定要手动配置参数,并根据业务是CPU 密集型还是IO 密集型来计算核心线程数。


七、 互动引导

如果你在面试中遇到这道题:

“如果线程池的队列满了,但核心线程都在忙,新来的任务会发生什么?”

你能精准地回答出 addWorker 的执行时机吗?

欢迎在评论区留下你的代码实现片段。点赞过百,我将开源这套手写的“极简版 ThreadPoolExecutor”完整工程代码(包含单元测试)!

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

MindSpore 实战指南:全场景 AI 开发落地案例分享

一、MindSpore工业级优化&#xff1a;让模型训练与推理更高效在工业场景中&#xff0c;模型的训练效率、推理速度、资源占用率直接影响项目落地成本与效果。MindSpore内置了丰富的工业级优化能力&#xff0c;无需开发者深入底层硬件细节&#xff0c;即可通过简单的API调用实现性…

作者头像 李华
网站建设 2026/2/12 12:19:03

对于jvm调优的思路

我们对这个jar包&#xff0c;要监控什么&#xff0c;为什么jvm需要调优&#xff0c;怎么监控指标&#xff0c;什么时候需要去进行调优呢. 这里先做一个第一版的总结&#xff1a; 我们知道&#xff0c;在java、中&#xff0c;是不用去进行内存管理的。 内存管理&#xff0c;由GC…

作者头像 李华
网站建设 2026/2/12 5:14:35

智能网关DTU如何实现灌区信息化管理的节能增效

在农业灌区管理中&#xff0c;水资源浪费、调度不精准、设备运维成本高是核心痛点。物通博联&#xff08;WideIOT&#xff09;智能网关DTU通过集成物联网、数据采集、无线通信等技术&#xff0c;构建灌区信息化管理平台&#xff0c;实现灌溉用水的精准调度与高效利用&#xff0…

作者头像 李华
网站建设 2026/2/13 23:21:55

矩阵的行列式是什么

矩阵的行列式&#xff08;determinant&#xff0c;简称 det&#xff09;是线性代数中一个非常重要的概念。它只定义在方阵&#xff08;行数等于列数的矩阵&#xff09;上&#xff0c;是一个从矩阵中计算出来的标量值&#xff08;就是一个数字&#xff09;。行列式反映了矩阵在线…

作者头像 李华
网站建设 2026/2/12 9:33:55

什么是WEB应用防火墙,云服务器有带吗

WEB应用防火墙&#xff08;WAF&#xff09;的定义WEB应用防火墙&#xff08;Web Application Firewall&#xff0c;简称WAF&#xff09;是一种专门保护网站或WEB应用的安全工具&#xff0c;通过监控、过滤和拦截HTTP/HTTPS流量&#xff0c;防御SQL注入、跨站脚本&#xff08;XS…

作者头像 李华