线程池简单源码思路手撕实现和关于参数设置

线程池简单源码思路手撕实现

importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.BlockingQueue;importjava.util.concurrent.TimeUnit;publicclassmyThreadPool{privateintcorePoolSize;privateintmaxPoolSize;privateinttimeout;privateTimeUnittimeUnit;publicBlockingQueue<Runnable>blockingQueue;privateRejectHandlerejectHandle;privateList<Thread>coreList=newArrayList<>();privateList<Thread>supportList=newArrayList<>();publicmyThreadPool(intcorePoolSize,intmaxPoolSize,inttimeout,TimeUnittimeUnit,BlockingQueue<Runnable>blockingQueue,RejectHandlerejectHandle){this.corePoolSize=corePoolSize;this.maxPoolSize=maxPoolSize;this.timeout=timeout;this.timeUnit=timeUnit;this.blockingQueue=blockingQueue;this.rejectHandle=rejectHandle;}voidexecute(Runnablecommand){if(coreList.size()<corePoolSize){CoreThreadthread=newCoreThread(command);coreList.add(thread);thread.start();return;}if(blockingQueue.offer(command)){return;}if(coreList.size()+supportList.size()<maxPoolSize){SupportThreadthread=newSupportThread(command);supportList.add(thread);thread.start();return;}if(!blockingQueue.offer(command)){rejectHandle.reject(command,this);}}classCoreThreadextendsThread{privatefinalRunnablefirstTask;CoreThread(RunnablefirstTask){this.firstTask=firstTask;}@Overridepublicvoidrun(){firstTask.run();while(true){try{Runnablecommand=blockingQueue.take();command.run();}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}classSupportThreadextendsThread{privatefinalRunnablefirstTask;SupportThread(RunnablefirstTask){this.firstTask=firstTask;}@Overridepublicvoidrun(){firstTask.run();while(true){try{Runnablecommand=blockingQueue.poll(timeout,timeUnit);if(command==null){break;}command.run();}catch(InterruptedExceptione){thrownewRuntimeException(e);}System.out.println(Thread.currentThread().getName()+"线程死掉了");supportList.remove(Thread.currentThread());}}}}
首先是重要的线程池参数:

核心线程数:定义不会被回收的核心线程数量

最大线程数:定义除了核心线程以外的临时线程,临时线程超过时间还没任务就会被销毁

阻塞队列:选择用什么阻塞队列,使用阻塞队列是防止在核心/辅助线程再去拿任务的时候一直while对cpu空转的损害

拒绝策略:在超过最大线程就会采取对应的拒绝策略-比如报错舍弃/静默丢弃/舍弃旧任务(或者也可以考虑持久化到mysql保存起来)

时间:这个是辅助线程没任务的存活时间。

时间单位:这个是辅助线程没任务的存活时间的单位。

线程工厂:用来决定线程的命名,创建线程的方式,管理线程优先级之类的。这里只是简单demo所以没有实现。

voidexecute(Runnablecommand){if(coreList.size()<corePoolSize){CoreThreadthread=newCoreThread(command);coreList.add(thread);thread.start();return;}if(blockingQueue.offer(command)){return;}if(coreList.size()+supportList.size()<maxPoolSize){SupportThreadthread=newSupportThread(command);supportList.add(thread);thread.start();return;}if(!blockingQueue.offer(command)){rejectHandle.reject(command,this);}}
这个是重要的线程池接受任务的过程:

核心线程没满就用核心线程,满了就先放到阻塞队列,阻塞队列满了就创建临时辅助线程,辅助线程也满了就考虑拒绝策略。

classCoreThreadextendsThread{privatefinalRunnablefirstTask;CoreThread(RunnablefirstTask){this.firstTask=firstTask;}@Overridepublicvoidrun(){firstTask.run();while(true){try{Runnablecommand=blockingQueue.take();command.run();}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}classSupportThreadextendsThread{privatefinalRunnablefirstTask;SupportThread(RunnablefirstTask){this.firstTask=firstTask;}@Overridepublicvoidrun(){firstTask.run();while(true){try{Runnablecommand=blockingQueue.poll(timeout,timeUnit);if(command==null){break;}command.run();}catch(InterruptedExceptione){thrownewRuntimeException(e);}System.out.println(Thread.currentThread().getName()+"线程死掉了");supportList.remove(Thread.currentThread());}}}}

这里有firsttask的任务主要是在线程刚创建就来执行,减少再去阻塞队列拿的麻烦,核心线程和辅助线程从阻塞队列拿任务的方法分别是take()和poll()主要是后者限制时间,辅助线程执行完就会被销毁这里是发生在run()执行完。而take()永久等待出不来就不会被销毁。

然后看一下拒绝策略
packagetech.insight;/** * @author gongxuanzhangmelt@gmail.com **/publicinterfaceRejectHandle{voidreject(RunnablerejectCommand,MyThreadPoolthreadPool);}
packagetech.insight;/** * @author gongxuanzhangmelt@gmail.com **/publicclassThrowRejectHandleimplementsRejectHandle{@Overridepublicvoidreject(RunnablerejectCommand,MyThreadPoolthreadPool){thrownewRuntimeException("阻塞队列满了!");}}
packagetech.insight;/** * @author gongxuanzhangmelt@gmail.com **/publicclassDiscardRejectHandleimplementsRejectHandle{@Overridepublicvoidreject(RunnablerejectCommand,MyThreadPoolthreadPool){threadPool.blockingQueue.poll();threadPool.execute(rejectCommand);}}

只实现了报错丢弃和丢弃旧任务加入新任务。

importjava.util.concurrent.ArrayBlockingQueue;importjava.util.concurrent.TimeUnit;publicclassMain{publicstaticvoidmain(String[]args){myThreadPool myThreadPool=newmyThreadPool(2,10,1,TimeUnit.SECONDS,newArrayBlockingQueue<>(2),newThrowRejectHandle());for(inti=0;i<10;i++){finalintfi=i;myThreadPool.execute(()->{try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}System.out.println(Thread.currentThread().getName()+" "+fi);});}}}

main里初始化线程池,以及添加10个任务,以上就是线程池的简单实现。

接下来我记录一些自己看的扩展

关于参数该怎么设置?比如核心线程数和最大线程数和其他的?

线程数

首先看八股怎么说:

cpu密集型:核心线程数=cpu核心数加减1,最大线程数=cpu核心数加减1

io密集型:核心线程数=cpu核心数*2,最大线程数=CPU核心数 × 4 或更高

然后还有具体的场景:

比如在电商场景:流量波动极大。平时可能没什么人,一旦活动开启,流量瞬间暴涨。可以极端的把核心线程数设置为0(还是留一点但是占比小),全是临时线程没有任务就回收。

还有记录日志场景:这种一般没什么变化,就全部设置为核心线程数就好,最大线程数等于核心线程数。

阻塞队列

阻塞队列选有界的如ArrayBlockingQueueLinkedBlockingQueue(n),因为无界队列存在内存溢出风险,还有**SynchronousQueue:** 之前提到的电商核心线程为 0 时常用。它不存任务,直接“手递手”交给线程。

关于存活时间

电商/高并发场景:建议设置得稍长一些(如 60s)。因为流量往往是波动性的,频繁地销毁和重新创建线程是非常消耗 CPU 的。

后台低频任务:建议设置得短一些(如 1s 或 10s)。任务处理完赶紧释放资源。

默认参考:Java 自带的CachedThreadPool默认是60s,这是一个非常合理的平衡点点。

可以理解为核心线程少就长一点,多就短一点。

拒绝策略

当线程池满了(最大线程已开,队列也塞满了),新来的任务怎么办?Java 提供了四种标准策略:

  1. AbortPolicy(默认):直接抛出RejectedExecutionException
    • 适用:关键业务,必须让调用方知道任务失败了。
  2. CallerRunsPolicy(调用者运行):谁提交的任务,谁自己去执行。
    • 适用:最重要的兜底方案。它会减慢生产者的速度(因为生产者去干活了,没空发任务了),起到一种天然的“降级”和“限流”作用。
  3. DiscardPolicy:直接丢掉任务,不报任何错。
    • 适用:比如打印无关紧要的日志、监控埋点,丢了就丢了。
  4. DiscardOldestPolicy:丢弃队列里排队最久(最老)的任务,把当前任务塞进去。
    • 适用:具有“时效性”的任务,比如实时行情数据,旧的数据没意义了。

根绝业务性质来选择拒绝策略。

还有美团的动态线程池是怎么搞的?

先讲讲为什么有这个需求?

所谓的“美团动态线程池”,核心本质就是:配置中心(如 Nacos/Apollo) + JDK 线程池原生 API + 监控告警

它不是创造了一种新的线程池技术,而是一套管理和运维线程池的架构方案。这个概念最早源于美团技术团队发表的一篇经典文章《Java线程池实现原理及其在美团业务中的实践》,后来成为了业界标准。

传统痛点:线程池参数(核心数、最大数、队列长度)通常写死在代码或配置文件里。一旦上线,发现流量太大导致队列积压,或者参数设置不合理导致 CPU 飙升,必须改代码 -> 重新打包 -> 重启服务。这在“双11”这种分秒必争的时刻是致命的。

动态线程池能做到什么:

热更新:像开关灯一样,运营/开发在后台修改参数,服务端的线程池瞬间生效,无需重启

全景监控:实时看到线程池在干嘛(队列堆了多少、当前活跃线程多少)。

具体是怎么实现的?

它的实现并不复杂,主要分为三步:管理、监听、修改

第一步:管理(注册中心)

也就是你要把系统里所有的线程池都管理起来。通常会做一个ThreadPoolManager,用一个Map把所有线程池存起来,每个线程池有个名字(比如order-pool,log-pool)。

第二步:监听(配置中心)

这是关键。利用Nacos、Apollo、Etcd等配置中心的能力。

  • 你在 Nacos 上修改配置:order-pool.coreSize = 20
  • 应用程序里的监听器(Listener)捕捉到配置变化。(只要改了主动推送)

第三步:修改(JDK API - 很多人不知道的秘密)

这是最核心的。很多人以为线程池创建了就不能改,其实JDK 的ThreadPoolExecutor原生就提供了 public 的 set 方法

这里还有一个点,默认的阻塞队列是final的想要修改长度只能自己实现阻塞队列并去掉final。

还要配合定时任务去监控读取运行时指标保证安全,也要监控队列使用率比如超过80%报警。

省流:

“其实参数设置没有绝对的标准,核心准则是:即不要让 CPU 闲置(吞吐量太低),也不要让队列积压导致 OOM(内存溢出)。

所以,动态线程池才是最终的解决方案。因为线上流量是未知的,我们无法在写代码时就预测出最完美的参数,只有具备了动态调整可视化监控的能力,才能让系统在面对突发流量时立于不败之地。”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1165470.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VBScript系统级自动化:使用WScript对象外部操控Office与模拟键盘输入

目录 第七章&#xff1a;WScript控制Office 7-1 WScript常用属性&#xff08;实例&#xff1a;交互性更好的自动拆分工作簿&#xff09; 7-2 WScript对象的常用方法 7-3 用WshShell对象控制程序&#xff08;另一种控制Word、Excel的方法&#xff09; 7-4 用WshShell做机器…

NPP 草原:南非图文巴,1949-1990 年,R1

NPP Grassland: Towoomba, South Africa, 1949-1990, R1 简介 本数据集包含七个文本格式 (.txt) 的数据文件。这些文件提供了在南非图文巴人工建立的草原稀树草原研究地点进行的生物量估算、土壤碳 (C)、氮 (N) 和磷 (P) 测量数据。该研究地点是长期施肥试验的一部分&#xf…

NPP 草原:南非图文巴,1949-1990 年,R1

NPP Grassland: Towoomba, South Africa, 1949-1990, R1 简介 本数据集包含七个文本格式 (.txt) 的数据文件。这些文件提供了在南非图文巴人工建立的草原稀树草原研究地点进行的生物量估算、土壤碳 (C)、氮 (N) 和磷 (P) 测量数据。该研究地点是长期施肥试验的一部分&#xf…

GEE初学:谷歌地球引擎GEE入门指南(最新注册全流程)

引言 谷歌地球引擎(Google Earth Engine, GEE)是一个用于全球尺度地理空间数据分析的强大平台。该平台提供数十年卫星影像和环境数据集的免费访问权限,推动遥感分析的普及化,并支持海量数据的云端处理。 本教程将带您完成从账号创建到首个资源(asset)可视化的完整流程,…

AI应用架构师打造的AI驱动虚拟旅游,树立行业标杆

从0到1构建AI驱动的虚拟旅游应用:AI应用架构师的实战指南 摘要/引言 问题陈述 随着人们对旅游体验多样化需求的增长,传统的实体旅游受到时间、空间以及各种现实因素的限制。如何突破这些限制,为用户提供沉浸式、个性化且不受地理和时间约束的旅游体验,成为旅游行业亟待解…

现代高级语言 JIT 编译优化技术——逃逸分析(Escape Analysis)

现代高级语言 JIT 编译优化技术——逃逸分析&#xff08;Escape Analysis&#xff09;逃逸分析的定义 逃逸分析&#xff08;Escape Analysis&#xff09; 是一种在编译期间&#xff08;对于Java等语言是在即时编译阶段&#xff09;进行的静态分析技术。它的核心目的是分析一个对…

CRM系统如何通过AI与自动化重塑企业销售效能

在数字化转型浪潮中&#xff0c;客户关系管理&#xff08;CRM&#xff09;系统已从简单的客户信息记录工具&#xff0c;演进为企业运营的核心中枢。一款价值型CRM&#xff0c;其关键在于能否将前沿技术深度融入业务场景&#xff0c;实现降本增效。以建广数科自主开发的智盈客CR…

.Net 中的 ActivatorUtilitiesConstructor 特性

.Net 中的 ActivatorUtilitiesConstructor 特性 [ActivatorUtilitiesConstructor] 是 .NET 依赖注入中的一个特性&#xff0c;用于指导 Microsoft.Extensions.DependencyInjection&#xff08;MSDI&#xff09;在类型有多个构造函数时&#xff0c;选择哪个构造函数进行实例化。…

Open Code教程(四)| 高级配置与集成

Open Code教程&#xff08;四&#xff09;| 高级配置与集成OpenCode 高级配置与集成一、前言二、本地模型配置方式一&#xff1a;Ollama&#xff08;推荐&#xff09;方式二&#xff1a;LM Studio方式三&#xff1a;llama.cpp本地模型推荐三、AGENTS.md 配置创建方式推荐结构高…

django-flask基于python的大学生班级档案管理系统

目录django-flask基于python的大学生班级档案管理系统摘要关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;django-flask基于python的大学生班级档案管理系统摘要 该系统基于Python语…

什么是SR-MPLS

文章目录为什么需要SR-MPLSSR-MPLS vs MPLSSR-MPLS的工作原理从SR-MPLS到SRv6SR-MPLS&#xff08;Segment Routing MPLS&#xff0c;基于MPLS转发平面的段路由&#xff09;是基于源路由理念而设计的在网络上转发数据包的一种协议。SR-MPLS的核心思想是将报文转发路径切割成不同…

救命神器10个一键生成论文工具,专科生毕业论文轻松搞定!

救命神器10个一键生成论文工具&#xff0c;专科生毕业论文轻松搞定&#xff01; AI 工具如何让论文写作变得轻松 对于许多专科生来说&#xff0c;毕业论文的撰写无疑是一道难以逾越的难关。从选题到开题&#xff0c;从查找到写作&#xff0c;每一个环节都可能让人感到力不从心。…

django-flask基于python的大学生创新计划项目管理web系统

目录Django-Flask 基于 Python 的大学生创新计划项目管理 Web 系统摘要关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;Django-Flask 基于 Python 的大学生创新计划项目管理 Web 系统…

打开软件出现找不到d3dx9_36.dll如何修复? 附免费下载方法

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

学霸同款2026 TOP8 AI论文软件:本科生毕业论文必备测评

学霸同款2026 TOP8 AI论文软件&#xff1a;本科生毕业论文必备测评 2026年学术写作工具测评&#xff1a;为何需要一份精准榜单&#xff1f; 随着AI技术在学术领域的深入应用&#xff0c;越来越多的本科生开始依赖AI工具辅助论文写作。然而&#xff0c;面对市场上琳琅满目的AI论…

django-flask基于python的大学生公益活动志愿服务系统的设计与实现

目录 摘要 关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; 摘要 随着社会对公益事业的关注度不断提升&#xff0c;大学生参与志愿服务活动的需求日益增长。传统的志愿服务管理方式…

软件打开提示找不到d3dx9_30.dll文件 如何修复? 附免费下载方法

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

django-flask基于python的大学生兼职网站的设计与实现

目录摘要关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;摘要 随着互联网技术的快速发展&#xff0c;在线兼职平台已成为大学生获取社会实践机会的重要渠道。本文基于Python技术栈&…

计算机毕业设计springboot罕见病科普交流平台 SpringBoot 驱动的“罕见病智慧科普与互动服务系统” 基于 SpringBoot 的“罕见病知识共享与病友互助平台”

计算机毕业设计springboot罕见病科普交流平台fhi4o8jo &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。当罕见病遇上互联网&#xff0c;信息鸿沟便有望被填平。面对种类繁多、资料…

Open Code教程(一)| 快速入门:安装配置与基础使用

Open Code教程&#xff08;一&#xff09;| 快速入门&#xff1a;安装配置与基础使用OpenCode 快速入门&#xff1a;安装配置与基础使用一、什么是 OpenCode核心特点开发团队发布时间线相关开源项目二、OpenCode vs Claude Code 对比选择建议三、网络环境说明需要科学上网无需科…