1. 引言
合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。
线程池的创建
我们可以通过ThreadPoolExecutor来创建一个线程池。
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);
创建一个线程池需要输入几个参数:
- corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
- runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。
- ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
- LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
- SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
- maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
- ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
- RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
- AbortPolicy:直接抛出异常。
- CallerRunsPolicy:只用调用者所在线程来运行任务。
- DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
- DiscardPolicy:不处理,丢弃掉。
- 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
- keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
- TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
向线程池提交任务
我们可以使用execute提交的任务,但是execute方法没有返回值,所以无法判断任务是否被线程池执行成功。通过以下代码可知execute方法输入的任务是一个Runnable类的实例。
threadsPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
我们也可以使用submit 方法来提交任务,它会返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。
Future<Object> future = executor.submit(harReturnValuetask);
try {
Object s = future.get();
} catch (InterruptedException e) {
// 处理中断异常
} catch (ExecutionException e) {
// 处理无法执行任务异常
} finally {
// 关闭线程池
executor.shutdown();
}
线程池的关闭
我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
只要调用了这两个关闭方法的其中一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于我们应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow。
3. 线程池的分析
流程分析:线程池的主要工作流程如下图:

从上图我们可以看出,当提交一个新任务到线程池时,线程池的处理流程如下:
- 首先线程池判断基本线程池是否已满?没满,创建一个工作线程来执行任务。满了,则进入下个流程。
- 其次线程池判断工作队列是否已满?没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。
- 最后线程池判断整个线程池是否已满?没满,则创建一个新的工作线程来执行任务,满了,则交给饱和策略来处理这个任务。
源码分析。上面的流程分析让我们很直观的了解了线程池的工作原理,让我们再通过源代码来看看是如何实现的。线程池执行任务的方法如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//如果线程数小于基本线程数,则创建线程并执行当前任务
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
//如线程数大于等于基本线程数或线程创建失败,则将当前任务放到工作队列中。
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
//如果线程池不处于运行中或任务无法放入队列,并且当前线程数量小于最大允许的线程数量,
则创建一个线程执行任务。
else if (!addIfUnderMaximumPoolSize(command))
//抛出RejectedExecutionException异常
reject(command); // is shutdown or saturated
}
}
工作线程。线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会无限循环获取工作队列里的任务来执行。我们可以从Worker的run方法里看到这点:
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) {
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
4. 合理的配置线程池
要想合理的配置线程池,就必须首先分析任务特性,可以从以下几个角度来进行分析:
- 任务的性质:CPU密集型任务,IO密集型任务和混合型任务。
- 任务的优先级:高,中和低。
- 任务的执行时间:长,中和短。
- 任务的依赖性:是否依赖其他系统资源,如数据库连接。
任务性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务配置尽可能小的线程,如配置Ncpu+1个线程的线程池。IO密集型任务则由于线程并不是一直在执行任务,则配置尽可能多的线程,如2*Ncpu。混合型的任务,如果可以拆分,则将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率,如果这两个任务执行时间相差太大,则没必要进行分解。我们可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先得到执行,需要注意的是如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行。
执行时间不同的任务可以交给不同规模的线程池来处理,或者也可以使用优先级队列,让执行时间短的任务先执行。
依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,如果等待的时间越长CPU空闲时间就越长,那么线程数应该设置越大,这样才能更好的利用CPU。
建议使用有界队列,有界队列能增加系统的稳定性和预警能力,可以根据需要设大一点,比如几千。有一次我们组使用的后台任务线程池的队列和线程池全满了,不断的抛出抛弃任务的异常,通过排查发现是数据库出现了问题,导致执行SQL变得非常缓慢,因为后台任务线程池里的任务全是需要向数据库查询和插入数据的,所以导致线程池里的工作线程全部阻塞住,任务积压在线程池里。如果当时我们设置成无界队列,线程池的队列就会越来越多,有可能会撑满内存,导致整个系统不可用,而不只是后台任务出现问题。当然我们的系统所有的任务是用的单独的服务器部署的,而我们使用不同规模的线程池跑不同类型的任务,但是出现这样问题时也会影响到其他任务。
5. 线程池的监控
通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用
- taskCount:线程池需要执行的任务数量。
- completedTaskCount:线程池在运行过程中已完成的任务数量。小于或等于taskCount。
- largestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了。
- getPoolSize:线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不+ getActiveCount:获取活动的线程数。
通过扩展线程池进行监控。通过继承线程池并重写线程池的beforeExecute,afterExecute和terminated方法,我们可以在任务执行前,执行后和线程池关闭前干一些事情。如监控任务的平均执行时间,最大执行时间和最小执行时间等。这几个方法在线程池里是空方法。如:
protected void beforeExecute(Thread t, Runnable r) { }
6. 参考资料
分享到:
相关推荐
├─面试必问-架构杀手锏——java混乱的日志体系 │ java混亂日志体系源码揭秘.mp4 │ ├─面试必问-深入微服务之SpringBoot&Docker1 │ 深入微服务之SpringBoot&Docker.mp4 │ └─面试必问-聊聊哈希算法与HashMap
【Java课程设计——“聊天室”实验报告】 一、聊天室设计要求 “聊天室”的设计旨在满足现代网络环境中高效沟通的需求。随着网络技术的飞速发展,人们越来越依赖网络进行交流。为了促进公司内部员工与客户之间的...
Matlab基础与应用获奖课件.pptx
2025免费微信小程序毕业设计成品,包括源码+数据库+往届论文资料,附带启动教程和安装包。 启动教程:https://www.bilibili.com/video/BV1BfB2YYEnS 讲解视频:https://www.bilibili.com/video/BV1BVKMeZEYr 技术栈:Uniapp+Vue.js+SpringBoot+MySQL。 开发工具:Idea+VSCode+微信开发者工具。
内容概要:本文详细介绍了pfc-flac耦合代码在深部应力环境中模拟巷道和煤层开挖的应用。pfc-flac耦合代码将颗粒流(PFC)和有限差分法(FLAC)相结合,分别用于微观颗粒间相互作用和宏观应力应变问题的处理。文中展示了具体的Python代码片段,解释了如何生成颗粒模型、定义接触模型、创建模拟区域、设置初始应力状态以及实现数据交换。此外,还讨论了耦合代码在处理煤岩损伤演化、开挖步进控制等方面的优势,并提供了实际应用中的优化建议和技术难点。 适合人群:从事岩土工程、矿山开采等领域研究的技术人员和科研工作者。 使用场景及目标:①模拟深部应力环境下的巷道和煤层开挖过程;②研究岩体应力重分布及其对开挖的影响;③提高模拟精度,更好地预测岩层破坏形态。 其他说明:尽管pfc-flac耦合代码在深部应力环境模拟中有显著优势,但也存在一些挑战,如内存消耗较大等问题。文中提到了一些优化措施,如调整参数以节省内存开销等。
内容概要:文章详细阐述了阻抗建模过程,分为不考虑锁相环影响和考虑锁相环影响两种情况。不考虑锁相环时,基于电路拓扑结构列出方程,通过谐波线性化方法建模,并将电流基频分量及其正负序谐波分量进行傅里叶变换至频域,再变换至dq坐标系下,最终求得正负序输出阻抗。考虑锁相环影响时,重点在于锁相环对abc坐标系向dq坐标系转换的作用,以及谐波分量对锁相环角度的影响,通过引入传递函数,推导出正负序谐波电压分量到输出扰动角的传递函数,进而求得更精确的输出阻抗。此外,还介绍了稳定性分析,通过诺顿等效和戴维南等效,依据奈奎斯特稳定判据确保系统稳定。 适合人群:电力电子、自动化等相关专业的研究人员和技术人员,尤其是对电力系统建模与稳定性分析感兴趣的读者。 使用场景及目标:①用于研究并网逆变器的阻抗特性;②提高对锁相环在电力系统中作用的理解;③为电力系统的稳定性分析提供理论支持。 阅读建议:本文涉及较多数学公式和专业术语,建议读者先掌握基本的电力系统理论和控制理论基础知识,结合相关文献深入理解公式推导过程。
SpringBoot的租房系统,你看这篇就够了(附源码)
MapGIS地质图件一般统一规定.doc
内容概要:本文详细介绍了如何在MATLAB/Simulink中搭建和调试800V直流母线、311V交流输出的虚拟同步发电机(VSG)控制系统。首先,文章解释了VSG的基本概念及其重要性,接着深入探讨了功率计算模块、机械方程模块和电压环控制的具体实现方法。文中提供了多个关键公式的代码实现,并强调了调试过程中需要注意的关键参数和常见错误。此外,还分享了一些实践经验,如避免过度调制、设置合理的虚拟惯量和阻尼系数、以及进行负载突变测试的方法。 适合人群:从事电力电子、电力系统仿真研究的技术人员,尤其是有一定MATLAB/Simulink基础的研发人员。 使用场景及目标:适用于需要理解和实现虚拟同步发电机控制系统的科研项目和技术开发。目标是帮助读者掌握VSG的工作原理和实现方法,提高仿真和实际应用的成功率。 其他说明:文章不仅提供了详细的理论推导和代码实现,还结合了大量的实践经验,使读者能够更好地应对实际工程中的挑战。同时,提醒读者在实际应用前进行全面的极端工况测试,确保系统的稳定性和可靠性。
2025免费微信小程序毕业设计成品,包括源码+数据库+往届论文资料,附带启动教程和安装包。 启动教程:https://www.bilibili.com/video/BV1BfB2YYEnS 讲解视频:https://www.bilibili.com/video/BV1BVKMeZEYr 技术栈:Uniapp+Vue.js+SpringBoot+MySQL。 开发工具:Idea+VSCode+微信开发者工具。
PLC关键工程师现场调试步骤.doc
PCCAN接口解决专题方案.doc
内容概要:本文详细介绍了如何使用COMSOL软件对110kV三相高压电缆周围的电磁场进行建模和分析,探讨其对人体的影响。主要内容包括:建立电缆和人体的几何模型,选择适当的物理场模块并设置相关参数,进行精细的网格划分,以及最终求解和结果分析。文中不仅提供了具体的MATLAB和Java代码片段,还讨论了实际工程中需要注意的关键细节和技术难点,如电流密度、磁场强度的计算,网格划分策略,求解器设置等。通过对电场和磁场分布的深入分析,得出电缆周围电磁场对人体的具体影响及其安全性评价。 适合人群:从事电力系统设计、电磁兼容性研究的专业人士,以及对电磁场仿真感兴趣的科研工作者。 使用场景及目标:适用于电力设施建设前期的电磁环境评估,帮助工程师优化电缆布局,确保电磁辐射在安全标准范围内,保障公共健康。此外,也可作为教学案例,帮助学生掌握电磁场仿真技术和COMSOL软件的应用。 其他说明:文章强调了实际工程应用中的注意事项,如材料参数的选择、网格划分的合理性、求解器配置等,这些都是确保仿真结果准确性的重要因素。同时,通过具体实例展示了如何利用COMSOL进行复杂电磁场问题的研究,为相关领域的进一步探索提供了宝贵的经验和参考。
内容概要:“妈妈杯”是全国大学生数学建模竞赛的俗称,由中国工业与应用数学学会(CSIAM)主办。该竞赛面向全国普通高校全日制在校本科生和研究生,通常于每年11月左右举行。比赛形式为团队赛,每队3人,在规定时间内完成数学建模题目并提交论文。竞赛题目涉及实际问题,要求参赛者运用数学建模、计算机编程和论文写作能力解决问题。文中详细介绍了比赛的报名方式、备赛经验和往届题目。报名时间为每年10月左右,费用约为200~400元/队。备赛建议包括团队分工、核心技能提升、赛前模拟训练等方面。往届题目涵盖社会热点问题,如城市垃圾分类、新能源汽车充电桩布局等。 适合人群:全国普通高校全日制在校本科生和研究生,尤其是对数学建模感兴趣的学生。 使用场景及目标:①了解“妈妈杯”全国大学生数学建模竞赛的具体情况,包括主办单位、参赛对象、比赛时间和形式;②掌握比赛的报名方式、费用和官方信息渠道;③学习备赛经验和技巧,如团队分工、核心技能提升、赛前模拟训练;④参考往届题目和社会热点问题,为参赛做好充分准备。 阅读建议:此资源详细介绍了“妈妈杯”全国大学生数学建模竞赛的各个方面,从比赛基本信息到备赛经验都有覆盖。建议参赛学生仔细阅读,结合自身情况进行团队分工和技能提升,并关注官方渠道以获取最新信息。
Linux操作系统试验基础指导书.doc
内容概要:本文详细介绍了永磁同步电机(PMSM)采用非奇异快速终端滑模控制(GFTSMC)进行速度控制的方法和仿真结果。首先解释了传统PI调节器和滑模控制存在的问题,特别是抖振和收敛速度慢。接着展示了GFTSMC的核心设计思路,包括滑模面设计、控制律实现以及参数选择。文中提供了具体的MATLAB代码片段,用于实现滑模面和控制律,并讨论了如何通过调整参数来优化性能。仿真结果显示,在负载突变情况下,GFTSMC相比传统方法表现出更快的响应时间和更低的抖振水平。此外,作者还分享了一些调试经验和注意事项,如使用sigmoid函数减少抖振、设置合理的参数范围等。 适合人群:从事电机控制系统研究与开发的技术人员,尤其是对永磁同步电机有深入了解并希望提高其抗扰能力和响应速度的研究者。 使用场景及目标:适用于需要高性能速度控制的应用场合,如无人机电调、电动汽车驱动系统等。目标是实现快速响应的同时确保系统的稳定性和平滑性。 其他说明:文中提到的实际案例基于3kW永磁同步电机的实验数据,强调了理论与实践相结合的重要性。同时指出,尽管GFTSMC表现优异,但在实际应用中仍需考虑电机参数辨识的准确性,以避免因参数失配导致的性能下降。
内容概要:本文详细介绍了利用COMSOL进行煤矿瓦斯抽采数值模拟的方法,特别是变渗透率模型与煤体变形之间的耦合效应。文章首先解释了如何在COMSOL中建立固体力学和达西流的耦合模型,通过引入动态渗透率公式(如指数型变渗透率模型),将煤体变形与瓦斯流动紧密联系在一起。接着讨论了边界条件的设置技巧,强调了钻孔周围网格加密的重要性以及求解器配置的优化方法。此外,文中还分享了一些实用的操作技巧,如使用探针功能实时监测压力变化,确保模拟结果的准确性。最后,作者通过实际案例展示了模型的有效性和应用价值,指出该模型能够更好地预测瓦斯流动路径和抽采效果。 适合人群:从事煤矿开采、瓦斯治理及相关领域的科研人员和技术工程师。 使用场景及目标:适用于需要精确模拟煤矿瓦斯抽采过程的研究项目,旨在提高抽采效率并保障矿井安全。通过该模型,研究人员可以深入理解煤体变形与瓦斯流动之间的复杂关系,从而优化抽采工艺。 其他说明:文章不仅提供了详细的建模步骤和技术细节,还分享了许多实践经验,帮助读者避免常见错误,提升模拟的成功率。同时,作者鼓励进一步探索多物理场耦合的可能性,如加入温度场的影响,以获得更加全面的理解。
内容概要:本文详细介绍了直流电机的传递函数及其在Matlab环境下的模糊PID控制算法实现。首先,阐述了直流电机传递函数的基本概念,描述了输入电枢电压与输出转速之间的动态关系。接着,解释了模糊控制PID算法的工作原理,包括模糊化、模糊规则制定、模糊推理与解模糊等步骤。然后,展示了具体的Matlab代码实现,涵盖了传递函数定义、模糊控制器设计、仿真过程以及绘图展示控制效果。通过对比实验数据,证明了模糊PID控制在应对负载突变时的优势。 适合人群:对自动控制理论有一定了解并希望通过Matlab实现具体控制算法的研究人员和技术人员。 使用场景及目标:适用于需要精确控制直流电机转速的应用场景,如工业自动化生产线、机器人控制系统等。主要目标是提高系统的鲁棒性和适应性,尤其是在面对非线性或不确定性的环境中。 其他说明:文中提供的Matlab代码可以直接运行,便于读者理解和实践。同时,强调了在设计模糊控制器时需要注意的一些关键点,如隶属函数的选择和规则库的设计,确保控制效果最优。
In MATLAB, an EW (Exponentially Weighted) trading strategy involves using moving averages, a common technique for smoothing price data and identifying trends. This strategy can be implemented in MATLAB to generate buy and sell signals based on how the current price relates to the exponentially weighted moving average. Here's a breakdown of how to implement an EW trading strategy in MATLAB: 1. Data Preparation: Load historical price data: Import your financial data into MATLAB. This data should include the closing price of the asset you intend to trade. Calculate the exponentially weighted moving average (EWMA): Use the ewma function in MATLAB to calculate the EWMA. The ewma function takes the price data and a "smoothing factor" as input. The smoothing factor controls the speed at which th
内容概要:本文深入探讨了AUTOSAR OS中的任务机制,详细介绍了Basic Task和Extended Task两种任务类型及其状态转换。Basic Task有Suspended、Ready、Running三种状态,Extended Task在此基础上增加了Waiting状态。文章还列举并解释了AUTOSAR OS任务的多个关键属性配置,如任务名称、激活次数限制、内存保护标识、优先级、调度模式、堆栈大小、任务类型、FPU使用、自动启动和时间保护等。这些配置项可通过Vector DaVinci Configurator工具进行设置,以满足不同的应用场景需求。; 适合人群:汽车电子软件工程师,尤其是对AUTOSAR OS有一定了解,希望深入了解任务调度机制的工程师。; 使用场景及目标:①理解Basic Task与Extended Task的区别及其状态转换机制;②掌握AUTOSAR OS任务的关键属性配置,优化任务调度和系统性能; 阅读建议:本文内容较为专业,建议读者结合实际项目经验,特别是AUTOSAR OS的配置和调试经验来阅读,以便更好地理解和应用文中提到的任务机制和配置方法。