深入源码分析 Java 线程池的实现原理

Token 费用爆炸?教你用 Semantic Cache(语义缓存)把 API 成本降低 80% 本文介绍了语义缓存(Semantic Cache)技术在大模型应用中的关键作用。传统Redis缓存基于精确匹配,无法处理用户提问的语义相似性,导致重复调用昂贵的LLM API。语义缓存通过将问题向量化并在向量空间中进行相似度匹配,可识别30%-50%的重复提问,直接返回历史答案。文章详细解析了技术原理(Embedding+向量搜索)、架构设计,并提供了基于Python和ChromaDB的50行核心实现代码。同时指出了生产环境中的三大挑战:阈值设定、隐私保护和多轮对话处理,并给出相应解决方案。该技术能以极低成 阅读详情

程序的运行,其本质上,是对系统资源(CPU、内存、磁盘、网络等等)的使用。如何高效的使用这些资源是我们编程优化演进的一个方向。今天说的线程池就是一种对CPU利用的优化手段。

网上有不少介绍如何使用线程池的文章,那我想说点什么呢?我希望通过学习线程池原理,明白所有 池化技术 的基本设计思路。遇到其他相似问题可以解决。

13467292-2d964345d3864ce9

池化技术

前面提到一个名词——池化技术,那么到底什么是池化技术呢?

池化技术简单点来说,就是提前保存大量的资源,以备不时之需。在机器资源有限的情况下,使用池化技术可以大大的提高资源的利用率,提升性能等。

在编程领域,比较典型的池化技术有:

线程池、 连接池、 内存池、对象池等。

本文主要来介绍一下其中比较简单的线程池的实现原理,希望读者们可以举一反三,通过对线程池的理解,学习并掌握所有编程中池化技术的底层原理。

13467292-61b57bd54e7ad374

创建一个线程

在Java的并发编程中,线程是十分重要的,在Java中,创建一个线程比较简单:

publicclassApp{publicstaticvoidmain(String[] args)throwsException{newThread(newRunnable() {@Overridepublicvoidrun(){ System.out.println("线程运行中"); } }).start(); }}

我们通过创建一个线程对象,并且实现Runnable接口就可以实现一个简单的线程。可以利用上多核CPU。当一个任务结束,当前线程就接收。

但很多时候,我们不止会执行一个任务。 如果每次都是如此的创建线程->执行任务->销毁线程,会造成很大的性能开销。

那能否一个线程创建后,执行完一个任务后,又去执行另一个任务,而不是销毁。这就是线程池。

这也就是池化技术的思想,通过预先创建好多个线程,放在池中,这样可以在需要使用线程的时候直接获取,避免多次重复创建、销毁带来的开销。

13467292-b6858d59b19b63f4

线程池的简单使用

以下代码,是在Java中创建线程池:

importjava.util.concurrent.*;publicclassApp{publicstaticvoidmain(String[] args)throwsException{ ExecutorService executorService =newThreadPoolExecutor(1,1,60L, TimeUnit.SECONDS,newArrayBlockingQueue<>(10)); executorService.execute(newRunnable() {@Overridepublicvoidrun(){ System.out.println("abcdefg"); } }); executorService.shutdown(); }}

Jdk提供给外部的接口也很简单。直接调用ThreadPoolExecutor构造一个就可以了,也可以通过Executors静态工厂构建,但一般不建议。

可以看到,开发者想要在代码中使用线程池还是比较简单的,这得益于Java给我们封装好的一系列API。但是,这些API的背后是什么呢,让我们来揭开这个迷雾,看清线程池的本质。

13467292-b27c2a11e1b20dc3

线程池构造函数

通常,一般构造函数会反映出这个工具或这个对象的数据存储结构。

13467292-45fb37a8a7fd46b0

如果把线程池比作一个公司。公司会有正式员工处理正常业务,如果工作量大的话,会雇佣外包人员来工作。

闲时就可以释放外包人员以减少公司管理开销。一个公司因为成本关系,雇佣的人员始终是有最大数。

如果这时候还有任务处理不过来,就走需求池排任务。

acc : 获取调用上下文

corePoolSize: 核心线程数量,可以类比正式员工数量,常驻线程数量。

maximumPoolSize: 最大的线程数量,公司最多雇佣员工数量。常驻+临时线程数量。

workQueue:多余任务等待队列,再多的人都处理不过来了,需要等着,在这个地方等。

keepAliveTime:非核心线程空闲时间,就是外包人员等了多久,如果还没有活干,解雇了。

threadFactory: 创建线程的工厂,在这个地方可以统一处理创建的线程的属性。每个公司对员工的要求不一样,恩,在这里设置员工的属性。

handler:线程池拒绝策略,什么意思呢?就是当任务实在是太多,人也不够,需求池也排满了,还有任务咋办?默认是不处理,抛出异常告诉任务提交者,我这忙不过来了。

13467292-2958897509482796

添加一个任务

接着,我们看一下线程池中比较重要的execute方法,该方法用于向线程池中添加一个任务。

13467292-35fad084fd3f7802

核心模块用红框标记了。

第一个红框: workerCountOf方法根据ctl的低29位,得到线程池的当前线程数,如果线程数小于corePoolSize,则执行addWorker方法创建新的线程执行任务;

第二个红框:判断线程池是否在运行,如果在,任务队列是否允许插入,插入成功再次验证线程池是否运行,如果不在运行,移除插入的任务,然后抛出拒绝策略。如果在运行,没有线程了,就启用一个线程。

第三个红框:如果添加非核心线程失败,就直接拒绝了。

这里逻辑稍微有点复杂,画了个流程图仅供参考

13467292-cce63a27bfd86913

接下来,我们看看如何添加一个工作线程的?

13467292-1a28b41a6f1913f7

添加worker线程

从方法execute的实现可以看出:addWorker主要负责创建新的线程并执行任务,代码如下(这里代码有点长,没关系,也是分块的,总共有5个关键的代码块):

13467292-f8034b30080d8c4c

第一个红框:做是否能够添加工作线程条件过滤:

判断线程池的状态,如果线程池的状态值大于或等SHUTDOWN,则不处理提交的任务,直接返回;

第二个红框:做自旋,更新创建线程数量:

通过参数core判断当前需要创建的线程是否为核心线程,如果core为true,且当前线程数小于corePoolSize,则跳出循环,开始创建新的线程

有人或许会疑问 retry 是什么?这个是java中的goto语法。只能运用在break和continue后面。

接着看后面的代码:

13467292-2aae5f01c5507749

第一个红框:获取线程池主锁。

线程池的工作线程通过Woker类实现,通过ReentrantLock锁保证线程安全。

第二个红框:添加线程到workers中(线程池中)。

第三个红框:启动新建的线程。

接下来,我们看看workers是什么。

13467292-e15a3ce6822f0410

一个hashSet。所以,线程池底层的存储结构其实就是一个HashSet。

13467292-d6fa62eacc0b5c8a

worker线程处理队列任务

13467292-c71f355acd4d25d7

第一个红框:是否是第一次执行任务,或者从队列中可以获取到任务。

第二个红框:获取到任务后,执行任务开始前操作钩子。

第三个红框:执行任务。

第四个红框:执行任务后钩子。

这两个钩子(beforeExecute,afterExecute)允许我们自己继承线程池,做任务执行前后处理。

到这里,源代码分析到此为止。接下来做一下简单的总结。

13467292-adb92e26aca849fe

总结

所谓线程池本质是一个hashSet。多余的任务会放在阻塞队列中。

只有当阻塞队列满了后,才会触发非核心线程的创建。所以非核心线程只是临时过来打杂的。直到空闲了,然后自己关闭了。

线程池提供了两个钩子(beforeExecute,afterExecute)给我们,我们继承线程池,在执行任务前后做一些事情。

线程池原理关键技术:锁(lock,cas)、阻塞队列、hashSet(资源池)

13467292-f5ac0092b5e04475

最后希望对你理解线程池有帮助。最后,留一个思考题,为什么线程池的底层数据接口采用HashSet来实现?

最后文末送波福利。现在加群即可获取Java工程化、高性能及分布式、高性能、高架构。性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的直播免费学习权限及领取相关资料,群号:835638062 点击链接加入群聊【Java高级架构】:https://jq.qq.com/?_wv=1027&k=5S3kL3v

OpenHarmony开发一个App,安装到BearPi-HM Micro开发板 建议先阅读我们这篇,这里面详细介绍了,以及hap包安装的全过程。本篇是介绍如何开发一个App,然后安装到开发板上,比如hap包,无法安装等问题。 阅读详情

相关推荐

第二章 HART硬件设计实战 — 基于SD2057的低功耗调制解调器电路详解与选型指南

本文详细解析了基于SD2057芯片的HART协议低功耗调制解调器硬件设计。通过对比分析,阐述了SD2057在成本与性能上的优势,并重点拆解了其核心电路、外围元件选型、原理图绘制要点及PCB布局的实战技巧,为工业现场仪表的智能通信模块开发提供了完整的低功耗解决方案。

gan8painter的博客 423

深入源码分析Java线程池的实现原理

转载自:https://mp.weixin.qq.com/s/-89-CcDnSLBYy3THmcLEdQ 程序的运行,其本质上,是对系统资源(CPU、内存、磁盘、网络等等)的使用。如何高效的使用这些资源是我们编程优化演进的一个方向。今天说的线程池就是一种对CPU利用的优化手段。   网上有不少介绍如何使用线程池的文章,那我想说点什么呢?我希望通过学习线程池原理,明白所有池化技术的基本设计思...

johnhuster的专栏 1611

医疗领域BGE-M3模型微调全流程:从数据准备到效果评估

本文详细介绍了医疗领域BGE-M3模型的微调全流程,从数据准备到效果评估。通过医疗数据微调,BGE-M3模型在临床问答、病历检索等场景中表现显著提升,能够准确理解医学术语之间的细微差别。文章还提供了实战练习代码和效果评估指标,帮助开发者快速掌握医疗文本嵌入模型的应用技巧。

weixin_29098367的博客 282

关于线程池的几个问题

一、如何实现非核心线程延迟死亡? 从runWorker方法可以看出,当getTask方法中获取不到执行任务的时候就会执行线程退出的操作 从下图可以看出返回null值的位置。 结论:非核心线程延迟死亡的条件为线程池处于运行状态&&当前线程数大于最大核心线程数&&获取任务时间超过keepAliveTime。 二、如何实现核心线程一直保活? 结论:通过阻塞队列take()方法让线程一直等待,使得Worker的run()方法一直阻塞,直到获取...

li_chunlong的专栏 555

保姆级教程:用Docker Compose一键部署WVP-PRO+ZLM+录像服务(含Nginx反代)

本文提供了一套完整的容器化GB28181视频平台部署方案,通过Docker Compose实现WVP-PRO、ZLMediaKit(ZLM)和录像服务的一键式部署。详细介绍了环境准备、架构设计、容器化部署实战以及高级配置优化,帮助用户快速搭建高性能视频监控管理平台,适用于安防行业和物联网设备管理场景。

weixin_30415113的博客 861

Versal ACAP NoC实战:像理解计算机网络一样配置AXI NoC,打通PS与PL数据通路

本文深入探讨了Versal ACAP NoC的配置与优化,通过计算机网络类比解析NoC架构,详细介绍了在VD100开发板上配置AXI NoC实现PS与PL数据交互的实战步骤。文章涵盖NoC组件功能、Vivado配置技巧、性能优化方法及常见问题解决方案,帮助开发者高效打通Versal芯片的数据通路。

weixin_42665255的博客 291

java线程池(ThreadPoolExecutor)源码解析二

addWorker() 方法 为什么workers使用HashSet和ReentraintLock而不使用并发的set addWorker() 方法 下面是增加了注释的addWorker方法的源码 /** 根据线程池当前状态和给定的边界条件(以核心线程数还是最大线程数为界)判断是否可以增加一个新的worker (worker是线程池内部的一个对象,是thread对象的一个...

u012941811的专栏 586

OpenEuler内核编译及替换

一、内核源码下载 在gitee上下载OpenEuler内核源码 下载地址 选择需要的内核版本源码压缩包进行下载 二、内核编译 1.解压缩 将下载好的源码进行解压 tar xvf openeuler-kernel-4.19.90-2107.1.0.tar.gz 2.清理源代码树 解压后得到一个名为kernel的内核源码文件夹,进入后执行命令 make mrproper 清理过去内核编译产生的config,第一次编译时可不执行此命令 3.生成内核配置文件.config 先将将系统原配置文件拷贝过来,原

m0_56602092的博客 2万+

Java集合 HashSet的原理及常用方法

目录 一. HashSet概述 二. HashSet构造 三. add方法 四. remove方法 五. 遍历 六. 合计合计 先看一下LinkedHashSet 在看一下TreeSet 七...

weixin_30724853的博客 613

一文快速教你如何下载国自然模版

5. 成功进入“相关文件查看”页面后,点击左侧的“项目申请文件“,如果你此次申请的是“面上项目”,选择对应的下拉框,找到对应的模版下载即可。6. 🫑们发现,在当前页面点击“青年科学基金项目”,左侧出现的是“原国家杰出青年科学基金项目”。7. 进入下一页后,点击“青年科学基金项目”,便可以在右侧找到🫑们要找的模版啦。2. 成功登陆后,点击1处的“首页”,在首页界面,再点击2处的“项目批准”。8. 点击上图3处的“文件详情”,就可以找到可以下载的模版啦(如下图)。3. 从“项目批准”进入后,点击“管理”。

薰珞婷紫小亭子的博客 2399

【即插即用模块】注意力篇 | TGRS 2025 | MFA聚特征+HPDown保细节+HFF融层级,三大模块各司其职,性能翻倍!

本文介绍了MTMLNet网络中的三大核心模块:MFA(多阶段特征聚合)、HPDown(混合池化下采样)和HFF(分层特征融合)。MFA通过双尺度深度卷积和注意力机制增强特征提取能力;HPDown结合平均池化和最大池化,有效保留下采样过程中的特征信息;HFF采用三注意力机制实现自适应特征融合。这三个模块协同工作,在红外小目标检测与分割任务中取得了SOTA效果。实验表明,各模块在特征提取、信息保留和融合方面均优于传统方法,可灵活应用于各类视觉任务中。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 286

Java线程池实现原理及源码分析

一、前言 线程的重要性便不需要在这里多言。我们开发的时候其实一直在使用线程,只是这个过程被我们忽略了,Tomcat处理每个请求时使用的就是Java的多线程,一个请求即一个线程。 线程的生命周期有五个阶段: 新建(New):当程序实例化了一个Thread对象后,该线程就处于新建状态中,此时JVM仅为其分配内存并初始化了成员变量的值; 就绪(Runnable):当线程对象调用了start()方法后该线程就处于就绪状态,JVM会为其创建方法调用栈和程序计数器,等待调度运行; 运行(Running):就绪

Armyyyyy丶的博客 457

基于Simulink实现电动汽车电机驱动系统的建模与仿真

通过上述步骤,我们成功实现了基于Simulink的电动汽车电机驱动系统的建模与仿真。该系统能够全面评估电机驱动系统的性能,验证控制策略的有效性,并通过优化设计提高车辆的动力性和效率。引入智能预测:结合人工智能技术,实现更智能的控制策略。扩展功能:增加对多种电机类型的支持,提升系统通用性。实验验证:将仿真模型应用于实际硬件,进行实验验证,评估其在实际工况下的表现。

xiaoheshang_123的博客 317

Java线程池实现原理和源码分析

本文章是从2019年11月下旬开始打开写的,一直拖到2020年的年尾才开始写,直到2021年年初才写完。时间太快也太慢~!依稀记得2019年10月份的时候某东从创业公司离职打算面试找工作,他问我线程池你会么?然后给我他发了一篇我2017年写的笔记《Java并发编程之线程池必用知识点》,他说就这么点?我当时想线程池也差不多就这么多吧~!2019年11月9号我和某东一起从大望路做815公交去燕郊。当时只是因为我正在学习一部分多线程相关的知识点,刚好公交车上没啥事情我俩就唠了唠。

stven_king的专栏 1498

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

java线程池实现原理的源码分析

java 中线程池的核心实现是ThreadPoolExecutor ,那么这个对象是如何实现的呢?在分析前一般最大的疑问是: 1、线程池的中线程对象如何被复用的? 猜测:线程池的中线程对象的的run方法应该是一个死循环,这样才能保证运行完一个任务后,线程对象不会被关闭 2、线程池如何优雅的替换的线程要执行的任务? 猜测:应该是通过某种方法不停的获取不同的任务对象(Runnable ),然后再线程对象的run方法调用该runnable 实例的run()方法 一、ThreadPoolExecuto

zhangshenglu1的博客 629

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

深入源码分析 Java 线程池实现原理

池化技术 池化技术简单点来说,就是提前保存大量的资源,以备不时之需。在机器资源有限的情况下,使用池化技术可以大大的提高资源的利用率,提升性能等。 在编程领域,比较典型的池化技术有:线程池、连接池、内存池、对象池等。 本文主要来介绍一下其中比较简单的线程池的实现原理,希望读者们可以举一反三,通过对线程池的理解,学习并掌握所有编程中池化技术的底层原理。 创建一个线程 在Ja...

204

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

线程池原理_深入源码分析Java线程池的实现原理

程序的运行,其本质上,是对系统资源(CPU、内存、磁盘、网络等等)的使用。如何高效的使用这些资源是我们编程优化演进的一个方向。今天说的线程池就是一种对CPU利用的优化手段。网上有不少介绍如何使用线程池的文章,那我想说点什么呢?我希望通过学习线程池原理,明白所有池化技术的基本设计思路。遇到其他相似问题可以解决。池化技术前面提到一个名词——池化技术,那么到底什么是池化技术呢?池化技术简单点来说,就是提...

weixin_29218509的博客 202
上一篇: 蚂蚁金服面试经历-前期准备
下一篇: 用SpringBoot集成Netty开发一个基于WebSocket的聊天室
普通网友
博客等级 码龄8年 55粉丝 197原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值