9 线程池

目录

1 线程池各参数

1.1 corePoolSize

1.2 maximunPoolSize

1.3 keepAliveTime

1.4 workQueue

1.5 RejectedExecutionHandler

2 线程池工作机制

2.1 流程

2.2 提交任务

3 相关问题

3.1 线程池核心线程数、最大线程数设置

3.2 ApiPost压测

3.3 为什么要用阻塞队列

4 源码分析

4.1 execute

 4.2 addWorker

 4.3 runWorker

 4.4 getTask

4.5 异常处理


为什么要使用线程池?

        1 重复利用已创建的线程,减少线程创建和销毁带来的开销

        2 提高响应速度:任务可以不用等待线程创建就能立即执行(T1 创建线程 T2执行任务 T3销毁线程),若T1+T3>T2,可以通过线程池提高响应速度

        3 提高线程可管理性:线程是稀缺资源,会降低系统稳定性,通过线程池可以对线程进行统一分配、调优和监控

1 线程池各参数

1.1 corePoolSize

        核心线程数(CPU核数),任务提交后

                线程数若小于corePoolSize,会一直创建(之前空闲的线程也不会使用)

                线程数=corePoolSize,提交的任务会提交到队列中,等待被被执行(非核心线程or核心线程)

                执行了线程池的 prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程

1.2 maximunPoolSize

        线程中允许的最大线程数;当前阻塞队列满且提交新任务,则会开辟非核心线程去执行任务(前提是当前线程<maximunPoolSize)

1.3 keepAliveTime

        线程空闲的闲置时间(当没其它任务时,允许存活的时间)

1.4 workQueue

        当线程池的线程数超过corePoolSize时,任务会进入BlockingQueue进行阻塞

        一般来说,选择有界队列,使用无界队列会带来如下影响:

                1 线程数达到corePoolSize时,新任务会被放到无界队列中等待,线程池中的线程数不会超过corePoolSize(maximunPoolSize会无效)

                2 由于1,maximunPoolSize会无效

                3 由于1,keepAliveTime会无效

                4 无界队列会占用系统资源,有界队列有利于预防资源耗尽

                一般使用:ArrayBlockingQueue、LinkedBlockingQueue、SychronousQueue、PriorityBlockingQueue

1.5 RejectedExecutionHandler

        当阻塞队列满,且没有空闲线程工作时,如果继续提交任务,会采取一种策略处理该任务:

                AbortPolicy:直接抛异常(默认策略)

                CallerRunsPolicy:用调用者所在的线程执行

                DiscardOldestPolicy:丢弃阻塞队列靠前的任务,并执行当前任务

                DiscardPolicy:直接丢弃任务

2 线程池工作机制

2.1 流程

注意:

        1 提交任务时,不管核心线程数是否空闲,只要线程数<corePoolSize,线程池都会优先创建线程

        2 ThreadPoolExecutor是非公平的,队列满之后提交的Runnable可能比队列中靠前的先执行

2.2 提交任务

        1 execute

        2 submit:本质是1,只不过操作的是Future对象

3 相关问题

3.1 线程池核心线程数、最大线程数设置

        corePoolSize:核心线程数,常驻线程数

        maximumPoolSize:线程池能开辟的最大线程数

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

        CPU密集型:

                线程在执行任务时,会一直利用CPU,要避免发生线程的上下文切换,通常设置为Ncpu+1

        IO密集型:

                线程执行任务时使用CPU的时间<<接口响应时间;可以考虑设置为 2*Ncpu

获取系统CPU个数(Ncpu):Runtime.getRuntime().availableProcessors()  //逻辑核

计算核心线程数核心理论:Nthreads = CPU核数*(1+线程总等待时间/线程总运行时间)

总结:

        1 CPU密集型:CPU核数+1,充分利用CPU,不至于有太多上下文切换

        2 IO密集型:建议压测,或用公式计算一个理论值

        3 对于核心业务(访问频率高),可以把核心线程数设置为压测的结果,最大线程数可以和核心线程差不多或更大一些;譬如:压测后发现500线程最佳,600线程也许还行,则最大线程数可以设置为600

        4 对于非核心业务(访问率不高),核心线程可以设置比较小,避免操作系统维护不必要的线程,最大线程可以设置为计算、压测出的结果

3.2 ApiPost压测

ApiPost压测结果:

        1 尝试不同的线程数,然后进行压测,或把可以接收的线程数设置为核心线程数

        2 若请求一直不断,则核心线程数可以设置为最大线程数,一开始就创建很多线程,高效处理

        3 如果平时压力小,偶尔高并发,则可设置10核心线程数,500最大线程数

        4 核心业务,总有500请求过来,核心线程数可以设置为500;最大线程数可以设置为500,也可设置为800

3.3 为什么要用阻塞队列

        线程池中的线程,从创建线程并领取任务执行后,执行完时,后续会一直从阻塞队列中获取任务,线程为了不消亡,会在获取队列时阻塞(核心线程不消亡;非核心线程根据keepAliveTime决定存活时间)

4 源码分析

4.1 execute

 4.2 addWorker

private boolean addWorker(Runnable firstTask, boolean core) {retry:for (;;) {int c = ctl.get();int rs = runStateOf(c);if (rs >= SHUTDOWN &&! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty()))return false;for (;;) {int wc = workerCountOf(c);if (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))return false;//1 尝试增加工作线程计数;未成功重新尝试if (compareAndIncrementWorkerCount(c))   break retry;c = ctl.get();  // Re-read ctlif (runStateOf(c) != rs)continue retry;}}boolean workerStarted = false;boolean workerAdded = false;Worker w = null;try {//2 创建Work对象,封装要执行的任务firstTaskw = new Worker(firstTask);final Thread t = w.thread;if (t != null) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {int rs = runStateOf(ctl.get());//线程池未处于关闭状态if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {if (t.isAlive())throw new IllegalThreadStateException();//3 在锁状态下将新的worker对象添加到工作线程列表workers.add(w);int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;}} finally {mainLock.unlock();}//4 若任务被添加到线程列表,则执行该任务if (workerAdded) {t.start();workerStarted = true;}}} finally {if (! workerStarted)//5 若出现异常则调用addWorkerFailed方法处理异常addWorkerFailed(w);}return workerStarted;}

 4.3 runWorker

final void runWorker(Worker w) {Thread wt = Thread.currentThread();Runnable task = w.firstTask;//获取到任务后置空w.firstTask = null;//在任何时候可以响应中断???w.unlock();boolean completedAbruptly = true;try {//不断尝试获取任务并执行while (task != null || (task = getTask()) != null) {w.lock();//线程池终止时,所有线程都中断if ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())wt.interrupt();try {//记录日志等beforeExecute(wt, task);Throwable thrown = null;try {task.run();} catch (RuntimeException x) {thrown = x; throw x;} catch (Error x) {thrown = x; throw x;} catch (Throwable x) {thrown = x; throw new Error(x);} finally {afterExecute(task, thrown);}} finally {//执行完成,释放锁task = null;w.completedTasks++;w.unlock();}}completedAbruptly = false;} finally {processWorkerExit(w, completedAbruptly);}}

 4.4 getTask

private Runnable getTask() {boolean timedOut = false; // Did the last poll() time out?//自旋,获取任务for (;;) {int c = ctl.get();int rs = runStateOf(c);//线程池即将关闭if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {decrementWorkerCount();return null;}int wc = workerCountOf(c);boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;//工作线程数>最大线程数,清理工作线程if ((wc > maximumPoolSize || (timed && timedOut))&& (wc > 1 || workQueue.isEmpty())) {if (compareAndDecrementWorkerCount(c))return null;continue;}try {//返回队列任务Runnable r = timed ?workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :workQueue.take();if (r != null)return r;timedOut = true;} catch (InterruptedException retry) {timedOut = false;}}}

4.5 异常处理

private void processWorkerExit(Worker w, boolean completedAbruptly) {//如果是意外退出则workerCount--if (completedAbruptly) // If abrupt, then workerCount wasn't adjusteddecrementWorkerCount();//加锁统计执行的任务数 并移除WorkerSet中的Workerfinal ReentrantLock mainLock = this.mainLock;mainLock.lock();try {//记录执行的任务次数 然后重worker set中移除completedTaskCount += w.completedTasks;workers.remove(w);} finally {mainLock.unlock();}//判断线程池状态 根据状态是否需要终止线程tryTerminate();int c = ctl.get();//如果线程池状态是小于stop的 非停止状态if (runStateLessThan(c, STOP)) {//如果没有意外退出 需要根据是否运行核心线程数存活标识来是否销毁线程if (!completedAbruptly) {//如果设置了KeepAliveTime之后,核心线程也会被销毁int min = allowCoreThreadTimeOut ? 0 : corePoolSize;//如果核心线程最少保留0且队列不为空的情况下 //线程池并没停止 则保留一个工作线程if (min == 0 && ! workQueue.isEmpty())min = 1;//如果当前的工作线程总数大于需要的Worker数 那就直接返回if (workerCountOf(c) >= min)return; // replacement not needed}//新建一个worker 核心线程addWorker(null, false);}
}

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

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

相关文章

初识Java篇

1.介绍Java语言 1.1Java是什么 Java是一种优秀的程序设计语言&#xff0c;它具有令人赏心悦目的语法和易于理解的语义。 不仅如此&#xff0c;Java还是一个有一系列计算机软件和规范形成的技术体系&#xff0c;这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境&am…

小知识(5) el-table行样式失效问题

一、实现效果 子级呈现不同颜色去区分 二、最初代码 tips: 我这里使用的vue3 elementplus <el-table :row-class-name"tableRowClassName" >... </el-table>function tableRowClassName({ row, rowIndex }) {if (row.children.length 0) {return …

基于ElasticSearch+Vue实现简易搜索

基于ElasticSearchVue实现简易搜索 一、模拟数据 产品名称描述价格库存数量品牌名称智能手表智能手表&#xff0c;具有健康跟踪和通知功能。199.991000TechWatch4K智能电视4K分辨率智能电视&#xff0c;提供出色的画质。699.99500VisionTech无线耳机降噪无线耳机&#xff0c;…

html iframe 框架有哪些优缺点?

目录 前言&#xff1a; 用法&#xff1a; 理解&#xff1a; 优点&#xff1a; 嵌套外部内容&#xff1a; 独立性&#xff1a; 分离安全性&#xff1a; 跨平台兼容性&#xff1a; 方便维护&#xff1a; 缺点&#xff1a; 性能开销&#xff1a; 用户体验问题&#xf…

【网安大模型专题10.19】※论文5:ChatGPT+漏洞定位+补丁生成+补丁验证+APR方法+ChatRepair+不同修复场景+修复效果(韦恩图展示)

Keep the Conversation Going: Fixing 162 out of 337 bugs for $0.42 each using ChatGPT 写在最前面背景介绍自动程序修复流程Process of APR (automated program repair)1、漏洞程序2、漏洞定位模块3、补丁生成4、补丁验证 &#xff08;可以学习的PPT设计&#xff09;经典的…

独家揭秘微信视频号下载提取器,使用方法!

1&#xff1a;微信视频号下载提取器&#xff0c;需要先确认自己手机电脑版本是否支持视频号的观看和浏览 2:需要下载视频号的作品发给视频下载小助手&#xff0c;聊天窗口 3&#xff1a;打开小助手解析视频号视频链接&#xff0c;保存到手机相册或者电脑上 注意视频号电脑版…

适用于 Linux 和 Unix 的特权访问管理

凭据、SSH 密钥、服务帐户、数字签名、文件系统等内容构成了Linux 环境的关键部分&#xff0c;虽然大多数PAM供应商为基于Windows的环境提供无缝的特权访问管理&#xff0c;但它们的通用性不足以为Linux&#xff0c;Unix和*nix环境扩展相同的功能和功能。 Linux 中的root权限是…

wf-docker集群搭建(未完结)

系列文章目录 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、redis集群二、mysql集群三、nacos集群1. 环境要求2. 拉取镜像2.1. 拉取镜像方式配置集群2.2. 自定义nacos镜像配置集群 3 自定义…

基于PHP的图像分享社交平台

有需要请加文章底部Q哦 可远程调试 基于PHP的图像分享社交平台 一 介绍 此图像分享社交平台基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。平台角色分为用户和管理员。用户可注册登录&#xff0c;发布图像&#xff0c;修改个人信息&#xff0c;评论图像…

分享一下门店服务预约系统怎么做

随着科技的不断发展&#xff0c;越来越多的企业开始注重提高服务质量和效率。其中&#xff0c;门店服务预约系统成为了许多企业的选择。本文将探讨门店服务预约系统的意义、设计思路、实现方法、系统测试以及拓展案例&#xff0c;并总结门店服务预约系统设计和实现的重要性。 一…

国腾GM8775C完全替代CS5518 MIPIDSI转2 PORT LVDS

集睿致远CS5518描述&#xff1a; CS5518是一款MIPI DSI输入、LVDS输出转换芯片。MIPI DSI 支持多达4个局域网&#xff0c;每条通道以最 大 1Gbps 的速度运行。LVDS支持18位或24位像素&#xff0c;25Mhz至154Mhz&#xff0c;采用VESA或JEIDA格 式。它只能使用单个1.8v电源&am…

更改idea的JDK版本

有时候我们需要更改 idea 的 JDK 版本&#xff0c;这里告诉大家更改的方法&#xff0c;非常简单快捷&#xff0c;而且也不需要去找 JDK 的资源 1.在 idea 的左上角找到 File 选择 Peoject Structure 2.在页面左上角找到 Project &#xff0c;点击 SDK 的框&#xff0c;选择 A…

RISC-V架构——中断委托和中断注入

1、中断委托 1.1、中断委托的作用 &#xff08;1&#xff09;默认情况下&#xff0c;所有的陷入&#xff08;中断和异常&#xff09;都是在M模式下处理&#xff0c;然后再返回到发生陷入前的模式&#xff1b; &#xff08;2&#xff09;所有陷入都在M模式处理会涉及到模式切换…

将自己本地项目上传到git,增加IDEA操作

文章目录 一、初始化git仓库二、gitee创建仓库三、输入自己仓库的地址四、在添加所修改的文件可能的错误 五、合并需上传文件六、上传参考文档 一、初始化git仓库 在自己的项目中&#xff0c;命令行中输入 git init二、gitee创建仓库 新建仓库 设置仓库参数&#xff0c;设置…

智安网络|探索语音合成技术的未来:揭秘人工智能配音技术的背后

随着人工智能技术的迅猛发展&#xff0c;配音行业也迎来了人工智能配音技术的崭新时代。人工智能配音技术通过语音合成和自然语言处理等技术手段&#xff0c;实现了逼真的语音合成&#xff0c;为影视、广告和游戏等领域带来了新的可能性。 第一部分&#xff1a;语音合成技术的…

Linux创建临时文件mkstemp()tmpfile()

有些程序需要创建一些临时文件&#xff0c;仅供其在运行期间使用&#xff0c;程序终止后即行删除。 很多编译器程序会在编译过程中创建临时文件。GNU C 语言函数库为此而提供了一系列库函数。&#xff08;之所以有“一系列”的库函数&#xff0c;部分原因是由于这些函数分别继…

LabVIEW在 XY Graph中选择一组点

LabVIEW在 XY Graph中选择一组点 问题&#xff1a;有一个包含许多点的XY Graph&#xff0c;在程序开发中&#xff0c;对于显示XY Graph中的多个点&#xff0c;如何进行选取。最好能像图像处理中的ROI一样&#xff0c;并且它们的颜色可以更改&#xff0c;可以在其中选择一些ROI…

【Docker】Docker数据的存储

默认情况下&#xff0c;在运行中的容器里创建的文件&#xff0c;被保存在一个可写的容器层里&#xff0c;如果容器被删除了&#xff0c;则对应的数据也随之删除了。 这个可写的容器层是和特定的容器绑定的&#xff0c;也就是这些数据无法方便的和其它容器共享。 Docker主要提…

Loop Copilot:AI驱动,小白也能自己生成音乐?

01 项目介绍 Loop Copilot是一个使用自然语言生成音乐的系统。它不仅允许你使用自然语言来生成你想要的音乐风格、节奏或旋律&#xff0c;还支持通过多轮对话对已生成的音乐进行进一步的编辑和修改。包括对生成的音乐进行编辑修改、添加或删除乐器、加入音效等。 02 工作流程…

(三)(Driver)驱动开发之双机调试环境搭建及内核驱动的运行

文章目录 1. 驱动开发环境搭建2. 驱动开发新建项目及项目属性配置和编译3. 双机调试环境搭建3.1 安装虚拟机VMware3.2 配置Dbgview.exe工具3.3 基于Windbg的双机调试 4. 内核驱动的运行4.1 临时关闭系统驱动签名校验4.2 加载驱动 1. 驱动开发环境搭建 请参考另一篇:https://bl…