JAVA线程池(ThreadPoolExecutor)原理分析与使用

Java线程池(ThreadPoolExecutor)原理分析与使用 在我们的开发中“池”的概念并不罕见,有数据库连接池、线程池、对象池、常量池等等。下面我们主要针对线程池来一步一步揭开线程池的面纱。使用线程池的好处1、降低资源消耗 可以重复利用已创建的线程降低线程创建和销毁造成的消耗。 2、提高响应速度 当任务到达时,任务可以不需要等到线程创建就能立即执行。 3、提高线程的可管理性 线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性 阅读详情

在我们的开发中,“池”的概念并不罕见,有数据库连接池、线程池、对象池、常量池等。下面我们主要针对线程池来一步一步揭开线程池的面纱。


使用线程池的好处

(1)降低资源消耗

可以重复利用自己创建的线程降低线程创建和销毁造成的消耗。

(2)提高响应速度

当任务到达时,任务可以不需要等到线程创建就能立即执行。

(3)提高线程的可管理性

线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。


线程池的工作原理

首先,我们看下当一个新的任务提交到线程池之后,线程池是如何处理的?

(1)线程池判断核心线程池里的线程是否都在执行。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。

(2)线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果队列满了,则执行第三步。

(3)线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。


线程池饱和策略

这里提到了线程池的饱和策略,那我们就介绍下有哪些饱和策略?

(1)AbortPolicy    

为java线程池迷人的阻塞策略,不执行任务,而且直接抛出一个运行时异常,切记ThreadPoolExecytor.execute需要try catch,否则程序会直接退出。

(2)DiscardPolicy

直接抛弃,任务不执行,空方法

(3)DiscardOldestPilicy

从队列里面抛弃head的一个任务,并再次execute 此task。

(4)CallerRunsPolicy

在调用execute的线程里面执行此command,会阻塞入口。

(5)用户 自定义拒绝策略(最常用)‘

实现RejectedExecutionHandler,并自己定义策略模式

下我们以ThreadPoolExecutor为例展示下线程池的工作流程图:

1、如果当运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。

2、如果运行的线程等于或多余corePoolSize,则将任务加入BlockingQueue。

3、如果无法将任务加入BlockingQueue(队列已满),则在非corePool中创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

4、如果创建新的线程将使当前运行 的线程超出maximumPoolSize,任务将被拒绝,并调用RejectdeExecutionHandler.rejectedExecution()方法。

        ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。


关键方法源码分析

我们看看核心方法添加到线程池execute的源码如下:

下面我们继续看看addWorker是如何实现的:

addWorker之后是runworker,第一次启动会执行初始化传进来的任务firstTask;然后会从workQueue中取任务执行,如果队列为空则等到keepAliveTime这么长时间



我们看一下getTask是如何执行的



下面我们看下processWorkerExit是如何工作的


tryTerminate

processWorkerExit方法中会尝试调用TryTimeinate来终止线程池。这个方法任何可能导致线程池终止的动作后执行:比如比如减少workerCount或SHUTDOWN状态下从队列中移除任务。

shutdown这个方法会将runState置为SHUTDOWN,会终止所有空闲的线程。

shutdownNow方法将runState置为STOP。和shutdown方法的区别,这个方法会终止所有的线程。主要区别在于shutdown调用的interruptldleWorkers这个房,而showdownNow实际调用的是Worker类的interruptlfStarted方法:

他们的实现如下:


线程池的使用

线程池的创建

我们可以通过ThreadPoolExecutor来创建一个线程池

向线程池提交任务

可以使用两个方法想线程池提交任务,分别为execute()和submit()方法。

execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功,通过以下代码可知execute()方法输入的任务是一个Runnable类的实例。

submit()方法用于提交需要返回值的任务,线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而get(long timeout,TimeUnit Unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

关闭线程池

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。他们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是他们存在一定的区别,showdownNow首先将线程池的状态设置为STOP,然后尝试停止所有正在执行或暂停任务的线程,宁返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中端所有没有正在执行任务的线程。

只要调用了这两个方法中的任意一个,isShutdown方法就会返回true,当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于应该调用哪一种方法来关闭线程池,应该有提交到线程池的任务的特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定执行完,则可以调用shutdownNow方法。


合理的配置线程池

要想合理的配置线程池,就必须首先分析任务特性,可以从以下几个角度来分析。

1、任务的性质:CPU密集型任务、IO密集型任务和混合型任务。

2、任务的优先级:高、中和低。

3、任务的执行时间:长、中和短。

4、任务的依赖性:是否依赖其他系统资源,如数据库连接。

性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。由于IO密集型任务线程并不一定一直在执行人任务,则应配置尽可能多的线程,如2*Ncpu。混合型任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行时间相差不是太大,那么分解后执行的吞吐量将高于串行的吞吐量。如果这两个任务执行时间相差太大,则没有必要进行分解,可以通过

Runtime.getRuntime().availableProcessors()方法来获取当前设备的CPU个数。优先级不通过的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级有的任务先执行。

如果一直有优先级高的任务提交到队列中,那么优先级低的任务可能永远不能执行。执行时间不同的任务可以交个不同规模的线程池来处理,或者可以使用优先级队列。执行让执行时间短的任务先执行。依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。

建议使用有界队列。有界队列能增加系统的稳定性和预警能力,可以根据需要设大一点儿,比如几千。有时候我们系统里后套任务线程池的队列和线程池全满了,不断抛出任务的异常,通过排查发现是数据库出现了问题,导致执行SQL变得非常缓慢,因为后台任务线程池里的任务全是需要向数据库查询和插入数据的,所以导致线程池里的工作线程全部阻塞,任务积压在线程池里。如果当时我们设置成无界队列,那么线程池的队列就会越来越多,有可能会撑满内存,导致整个系统不可用,而不是后台任务出现问题。当然,我们的系统所有的任务是用单独服务器部署的,我们使用不同规模的线程池完成不同类型的任务,但是出现这样的问题是也会影响到其他任务。


线程池的监控

如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,可以根据线程池的使用状态快速定位问题。可以通过线程池提供的参数进行监控,在监控线程池的时候可以使用一下属性:

    taskCount:线程池需要执行的任务的数量

    completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount。

    largestPoolSize:线程池里曾经创建过最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,表示线程池曾经满过。

    getPoolSize:线程池的线程数量。如果线程池不销毁的话,线程池里的线程不会自动销毁,所以这个大小只增不减

    getActiveCount:获取活动 的线程数。

通过扩展线程池进行监控。可以通过继承线程池来自定义线程池,重写线程池的beforeExecute、afterExecute和terminated方法,也可以在任务执行前、执行后和线程池关闭前执行一些代码来进行监控。例如,监控任务的平均执行时间、最大执行时间和最小执行时间等。




JAVA线程池的实现原理、ThreadPoolExecutor源码分析 为了阅读方便,部分代码有所简化 创建 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, ... 阅读详情

相关推荐

cesium加载天地图、OSM、谷歌底图

以上示例是加载矢量底图,若要加载影像或者其他只需要更换相应的url,在WebMapTileServiceImageryProvider中还要更改layer参数。url中’‘vec_w’,一些其他风格,基于CRAT渲染,两种方法效果差别不大。有两种方法加载天地图分别是。对应投影样式,具体可看。还要谷歌的一些卫星底图。

qq_44897914的博客 2998

Java-线程池ThreadPoolExecutor之invokeAll()源码解读

平时工作中因为用到ExecutorService的频率略多,而在这之中invokeAll方法又是常用方法,因此这里详细解读下invokeAll方法。 首先贴上源代码: public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { if (tasks == null)

沮丧的南瓜 4215

Ultrascale/Ultrascale+ FPGA GTH IP及结构详解(一)

FPGA GTH Transceiver

houzi6320的博客 1万+

白话ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,

jzlhll123的专栏 449

Tailscale 自建 DERP 中继服务器

Tailscale自建DERP中继服务器

Accompagner的博客 4328

别再死磕V1了!手把手教你用WPS Web Office V3 SDK快速集成(附Java Demo避坑指南)

本文详细介绍了如何从WPS Web Office V1迁移到V3版本,重点讲解了V3在性能、安全性和扩展性上的优势,以及Java开发者如何快速集成V3 SDK。文章包含核心功能实现、高级功能适配(如Solon框架)和常见问题排查指南,帮助开发者高效完成迁移并避免常见陷阱。

weixin_27215337的博客 335

线程池invokeAll方法详解

线程池invokeAll方法详解问题起源与抽象问题排查与猜测猜测一:invokeAll 在异步执行后会不会同步等待线程执行完毕获取最终结果猜测二:队列里面可能存在第一次调用 invokeAll 执行了但没有删掉的任务,所以才会导致第二次放入队列失败两次猜测失败后的总结复查源码,真相大白问题解决方案参考 线上真实案例,多次调用线程池 ThreadPoolExecutor 的 invokeAll() ...

sinat_40409279的博客 1万+

G.6 PaddleHub实战篇{ERNIE实现文新闻本分类、ERNIE3.0 实现序列标注}【四】

百度飞桨:ERNIE 3.0 、通用信息抽取 UIE、paddleNLP的安装使用[一]_汀、的博客-优快云博客PaddleHub--飞桨预训练模型应用工具{风格迁移模型、词法分析情感分析、Fine-tune API微调}【一】_汀、的博客-优快云博客 PaddleHub--{超参优化AutoDL Finetuner}【二】_汀、的博客-优快云博客PaddleHub实战篇{词法分析模型LAC、情感分类ERNIE Tiny}训练、部署【三】_汀、的博客-优快云博客通过前面几篇文章大家都有一定了解,下面

丨汀、的博客 6185

Java线程池ThreadPoolExecutor详解(一篇就够了)

Executor框架主要由三部分组成,任务,任务的执行者,执行结果,ThreadPoolExecutor和ScheduledThreadPoolExecutor的设计思想也是将这三个关键要素进行了解耦,将任务的提交和执行分离。任务在和中任务是指实现了Runnable接口和Callable接口的类,中将任务转换成FutureTask类,中任务被转换成类,该类继承FutureTask,并重写了run方法,实现了延时执行任务和周期性执行任务。任务的执行者包括任务执行机制的核心接口Executor的接口的和。...

m0_67393039的博客 1985

Unity零基础到入门 ☀️| 【UI系统学习】认识Unity引擎中几种最常用 UI系统,这一篇文章就够用了!

轻松学会 Unity 的 UGUI基础控件 使用 Unity中的UI介绍 UI即User Interface(用户界面)的简称。泛指用户的操作界面,UI设计主要指界面的样式,美观程度。而使用上,对软件的人机交互、操作逻辑、界面美观的整体设计则是同样重要的另一个门道。好的UI不仅是让软件变得有个性有品味,还要让软件的操作变得舒适、简单、自由,充分体现软件的定位和特点。 据我所知Unity中有两种自带的UI,一种是UGUI,一种是GUI Legacy GUI:旧版 UI,只有两个组件:文字和图片,配合鼠标事件来

努力前行,总会成为自己心中的那道光 2万+

SAP S4 HANA:按单生产(MTO)场景中的COGS分割

本文介绍了SAP S/4HANA中提供的各种选项,以在销售订单/商品是按订单生产(以下称为MTO)生产流程中的成本对象时,在基于帐户的COPA环境中报告COGS分割信息。

adam915的博客 1191

并发编程:执行器:4、运行N个任务并处理全部返回结果(invokeAll() )

线程执行器.invokeAll() 运行N个任务并处理全部返回结果。

博风_刺耳的博客(网络记事本) 766

【全局路径规划模块运动学算法】无人机Kinodynamic A*算法、Kinodynamic RRT*算法(附C++代码)

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!本文先对Kinodynamic A*算法做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章提示:以下是本篇文章正文内容啃这个Kinodynamic A_star算法,建议先啃一啃高飞fast_planner那篇论文,把理论打好,如果还是比较迷,就先看Dijksta、再看A_star、再看hybrid A_star 三个的流程,因为往上资料比较多,再看这个Kinodynamic A_star算法,流程已经很相像了。如果。

qq_35635374的博客 2600

java线程池ThreadPoolExecutor原理源码分析

java线程池实现原理,线程池远吗分析

qq_34735262的博客 1849

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Java ThreadPoolExecutor线程池原理及源码分析

一、源码分析(基于JDK1.6) ThreadExecutorPool是使用最多的线程池组件,了解它的原始资料最好是从从设计者(Doug Lea)的口中知道它的来龙去脉。在Jdk1.6中,ThreadPoolExecutor直接继承了AbstractExecutorService,并层级实现了ExecutorService和Executor接口。 1.Executor Executor是

Schelor的专栏 3034

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Java线程池-ThreadPoolExecutor原理分析与实战

Java线程池-ThreadPoolExecutor原理分析与实战(转自)

Lf_Stafine的博客 239

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析

引出线程池 线程是并发编程的基础,前面的文章里,我们的实例基本都是基于线程开发作为实例,并且都是使用的时候就创建一个线程。这种方式比较简单,但是存在一个问题,那就是线程的数量问题。 假设有一个系统比较复杂,需要的线程数很多,如果都是采用这种方式来创建线程的话,那么就会极大的消耗系统资源。首先是因为线程本身的创建和销毁需要时间,如果每个小任务都创...

Java爬坑之路 318

Java线程池ThreadPoolExecutor原理、源码分析

为什么要使用线程池? 使用线程池相对于直接创建线程有以下优点: 资源管理:线程池可以管理线程的创建和回收,避免频繁地创建和销毁线程,从而减少资源开销。 线程复用:线程池中的线程可以被复用,避免了频繁地创建和销毁线程,提高了线程的利用率。 控制并发数:线程池可以限制线程的数量,避免线程数过多导致系统资源耗尽,从而避免了系统崩溃或变得不稳定。 队列机制:线程池通过使用任务队列来存储等待执行的任务,当线程池中的线程满了时,新的任务可以被放入队列中等待执行,避免了任务丢失。 线程生命周期管理:线程池可以对线程的生

qq_45061342的博客 1789

java并发编程之源码分析ThreadPoolExecutor线程池实现原理

如果你选择了IT行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把P8大佬用过的一些学习笔记(pdf)都整理在本文中了《Java中高级核心知识全面解析》加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0user code.*/

2401_86401467的博客 500

Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理

相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理 execute()是 java.util.concurrent.Execut...

Trust_FreeDom的专栏 834
上一篇: 学习-关系型数据库,非关系型数据库,分布计算系统
下一篇: securecrt工具介绍
self_defining
博客等级 码龄10年 31粉丝 82原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值