【多线程】线程池 详解

线程池 详解

  • 1. 线程池是什么
  • 2. 标准库中的线程池
  • 3. 实现线程池
  • 4. 面试题

1. 线程池是什么

虽然线程的创建和销毁的开销比较小, 但还是有的, 如果频繁的创建和销毁线程, 开销还是比较大的.解决: 线程池或者协程, 本文主讲线程池.

线程池: 把线程提前创建好, 放到池子里, 后面需要用到线程直接从池子里面取, 不必从系统申请, 
线程用完, 不是还给系统, 而是放到池子里面, 必备下次使用, 这样就减去了频繁创建销毁线程的开销.

举个栗子:

  • 一家快递店,店里没有雇人,而是每次有业务来了,就现场找一名同学过来把快递送了,然后解雇同学。这个类比我们平时来一个任务,起一个线程进行处理的模式。
  • 很快老板发现问题来了,每次招聘 + 解雇同学的成本还是非常高的。
  • 老板还是很善于变通的,于是决定最多雇10个人, 并且雇了 10 个人之后不再解雇, 当一个任务来时, 老板就看如果还没有雇到 10 个(就算已经雇的人是闲着的, 只要没雇到 10 个就会雇新人), 就雇 1 个人去送快递, 否则只是把业务放到一个本本上,等这 10 个快递人员有空闲的时候去处理。

这个就是我们要带出的线程池的模式。
线程池最大的好处就是减少每次启动、销毁线程的损耗。

为什么线程放到池子里面就比从系统申请释放更快 ?
这就涉及到操作系统的用户态与内核态:
在这里插入图片描述

自己写的代码运行在用户态, 有些代码需要通过调用操作系统的 API, 进一步的逻辑会在内核中执行.
比如 System.out.println, 本质上需要经过 write 系统调用进入到内核中, 在内核里面会执行一些逻辑, 在内核中运行的代码称为 “内核态” 运行的代码.

创建线程本身需要内核的支持, 创建线程本身就是在操作系统内核中创建 PCB, 调用 Thread.start 也要进入内核态运行.

而把创建好的线程放到 “池子里”, 由于 “池子” 是用户态实现的, 放到池子里/从池子里取这个过程不需要涉及到内核态, 用纯粹的用户代码就能完成, 一般认为纯用户态的操作效率比经过内核态处理的操作效率更高.
为什么 ?
不是说内核处理的效率一定真的低, 而是说代码进入内核态之后运行就不可控了, 你不知道此时内核背负了多少任务, 内核什么时候执行我们的任务, 什么时候运行完就不知道了, 有时快有时慢, 所以是不可控的.

2. 标准库中的线程池

  1. ThreadPoolExecutor
    构造方法:
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {
  • corePoolSize: 核心线程数 (正式员工数量)
  • maximumPoolSize: 最大线程数 (正式员工 + 临时工数量)
  • keepAliveTime: 闲置的线程数超过corePoolSize时, 临时工多长时间后被销毁
  • unit: 时间的单位 (s, ms, us)
  • workQueue: 任务队列, 通过 submit 方法将任务放到任务队列中
  • threadFactory: 线程工厂, 线程是怎么创建出来的
  • handler: 拒绝策略, 任务队列满了怎么办? 1. 忽略最新任务 2. 阻塞等待 3. 丢弃最老的任务 4. 抛异常…
  1. Executors
    标准库中简化版本的线程池, 本质是针对 ThreadPoolExecutor 进行封装, 还提供一些默认参数.
  • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
  • 返回值类型为 ExecutorService
  • 通过 ExecutorService.submit 可以注册一个任务到线程池中.
        ExecutorService pool = Executors.newFixedThreadPool(10);pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});

Executors 创建线程池的几种方式:

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
  • newSingleThreadScheduledExecutor: 创建一个单个线程带有定时器的线程池.
  • newWorkStealingPool: 创建一个任务抢占式的线程池.每个线程都有自己的任务队列, 当把自己的任务全执行完了, 就去其他线程的任务队列中 “窃取” 任务进行执行.(在日常生活中的话就是指帮助别人干一些工作)

所以加上 ThreadPoolExecutor 共有 7 种创建线程池的方法

3. 实现线程池

  • 核心操作为 submit, 将任务加入线程池中
  • 使用 Worker 类描述一个工作线程. 使用 Runnable 描述一个任务.
  • 使用一个 BlockingQueue 组织所有的任务
  • 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
  • 指定一下线程池中的最大线程数 maxWorkerCount; 当当前线程数超过这个最大值时, 就不再新增线程了.
class MyThreadPool {//任务直接用Runnable,不用另外创建类了//用阻塞队列来组织任务private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();//用来描述线程的类static class Worker extends Thread{private BlockingQueue<Runnable> queue=null;//利用构造方法获取任务队列public Worker(BlockingQueue<Runnable> queue){this.queue=queue;}@Overridepublic void run() {while(true){try {//如果队列为空则阻塞Runnable runnable=queue.take();runnable.run();} catch (InterruptedException e) {throw new RuntimeException(e);}}}}//用数组来当作池存储线程List<Thread> list=new ArrayList<>();//构造方法中创建若干线程放到线程池中public MyThreadPool(int n){for(int i=0;i<n;i++){Worker worker=new Worker(this.queue);//注意不要忘记startworker.start();//加入线程池中list.add(worker);}}//提交任务public void submit(Runnable runnable)  {try {this.queue.put(runnable);} catch (InterruptedException e) {throw new RuntimeException(e);}}public static void main(String[] args) {MyThreadPool pool=new MyThreadPool(10);for(int i = 1;i <= 100;i++){int n = i;//submit了100次,相当于100个任务进入了任务队列,每个线程分一些,很快就执行完了pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("myThreadPool: " + n);}});}}
}

4. 面试题

有一个程序, 这个程序需要并发的完成一些任务, 如果使用线程池的话, 线程池中的线程数设置为多少合适 ?

回答:

  • 我们要通过性能测试, 才能找到合适的值.
  • 例如写一个服务器程序, 服务器通过线程池处理用户请求, 就可以针对这个服务器进行性能测试, 比如构造一些请求发送给服务器, 要进行性能测试的话, 请求就要构造很多, 比如 每秒发送 500/1000/2000 个, 可以根据实际业务场景构造一个合适的值
  • 然后根据线程池中不同的线程数, 观察任务的处理速度, 程序持有的 CPU 占有率, 一般线程数越多, 执行速度越快, 但是 CPU 占有率越高, 需要找到一个程序速度能接收且 CPU 占有率也合理的平衡点.

为什么不让 CPU 占有率 过高 ?

对于线上服务器, 一定要留有一定的冗余以便随时应对可能的突发情况, 例如请求暴涨, 若本身就把 CPU 快占完了, 这是突然来了一波请求高峰,此时服务器可能直接就挂了.

总结: 自己实现线程池

  1. 能够描述任务 (Runnable)
  2. 能够组织任务 (BlockingQueue)
  3. 能够描述工作线程
  4. 组织线程
  5. 线程从任务队列中取任务执行

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

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

相关文章

下载配置 maven并在 idea 上应用

目录 一 maven 定义 二 Maven特点 三 Maven仓库 四 安装配置maven 步骤一:准备安装包,解压 步骤二:配置maven的环境变量 步骤三:测试maven的环境变量是否配置成功 步骤四:配置maven本地仓库 步骤五:阿里云、腾讯镜像配置 步骤六:全局配置idea的maven路径 步骤七:创建…

命令行git联网失败,但是实际可以联网

最近下载代码的时候发现总是告诉我连不上github的网页&#xff0c;但是我自己通过浏览器又可以上网&#xff0c;找了半天发现这个方法可以。 记录下这个代理 打开git bash 执行以下命令&#xff1a; git config --global http.proxy http://127.0.0.1:7890 git config --glob…

Python 图形化界面基础篇:添加复选框( Checkbutton )到 Tkinter 窗口

Python 图形化界面基础篇&#xff1a;添加复选框&#xff08; Checkbutton &#xff09;到 Tkinter 窗口 引言什么是 Tkinter 复选框&#xff08; Checkbutton &#xff09;&#xff1f;步骤1&#xff1a;导入 Tkinter 模块步骤2&#xff1a;创建 Tkinter 窗口步骤3&#xff1a…

【C++】封装unordered_map和unordered_set(用哈希桶实现)

前言&#xff1a; 前面我们学习了unordered_map和unordered_set容器&#xff0c;比较了他们和map、set的查找效率&#xff0c;我们发现他们的效率比map、set高&#xff0c;进而我们研究他们的底层是由哈希实现。哈希是一种直接映射的方式&#xff0c;所以查找的效率很快…

LeetCode——动态规划篇(一)

刷题顺序及思路来源于代码随想录&#xff0c;网站地址&#xff1a;https://programmercarl.com 目录 509. 斐波那契数 - 力扣&#xff08;LeetCode&#xff09; 70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 746. 使用最小花费爬楼梯 - 力扣&#xff08;LeetCode&a…

瞄准办公场景,未来智能靠“AI+耳机”后来居上?

如何在广阔红海中开拓出蓝海&#xff1f;未来智能或可作为参考案例。 作为TWS耳机玩家&#xff0c;未来智能成立于2021年&#xff0c;日前完成了由天际资本领投的数千万元Pre-A轮融资&#xff0c;这也是该公司成立以来完成的第二轮融资。 从成立时间来看&#xff0c;在广阔的…

Linux---应用层获取usb设备描述信息通过endpoint地址数据通讯

文章目录 &#x1f308;应用层获取USB设备信息总体思路&#x1f308;应用层代码实例&#x1f308;实例测试&#x1f308;应用层通过endpoint进行数据读写 &#x1f308;应用层获取USB设备信息总体思路 应用层可以打开USB设备的节点&#xff0c;读取包括USB设备的配置&#xff…

deepspeed训练报错torch.distributed.elastic.multiprocessing.errors.ChildFailedError

测试场景&#xff1a;使用deepspeed框架训练gpt模型 问题&#xff1a; 报错torch.distributed.elastic.multiprocessing.errors.ChildFailedError 具体见截图&#xff1a;

UMA 2 - Unity Multipurpose Avatar☀️六.Advanced Occlusion高级遮挡功能解决皮肤服饰穿模

文章目录 🟥 本节功能效果展示🟧 基础项目配置🟨 本节项目配置🟩 配置MeshHideAsset1️⃣ 创建MeshHideAsset2️⃣ 配置SlotDataAsset3️⃣ 配置遮挡信息🟦 将 MeshHideAsset 配置到 Recipe🟥 本节功能效果展示 未遮挡前的穿模问题: 遮挡后效果:

厂商征集 | 2023年中国RPA市场洞察研究报告正式启动

RPA中国基于在科技行业的资源积累&#xff0c;以及对各领域「技术领导者」、「技术应用者」、「产品服务商」的深度调研&#xff0c;2023年&#xff0c;我们重点推出MI报告 ( Market Insight )、CI Vendor报告&#xff08;Comprehensive Influence Vendor&#xff09;两个系列。…

【Jmeter】什么是BeanShell?

一、什么是BeanShell&#xff1f; BeanShell是用Java写成的,一个小型的、免费的、可以下载的、嵌入式的Java源代码解释器&#xff0c;JMeter性能测试工具也充分接纳了BeanShell解释器&#xff0c;封装成了可配置的BeanShell前置和后置处理器&#xff0c;分别是 BeanShell Pre…

NoSQL之Redis配置与优化(一)

关系数据库与非关系型数据库 &#xff1a; ●关系型数据库&#xff1a; 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于…

FP7122 具有平均模式恒定电流控制的LED驱动器芯片

FP7122 具有平均模式恒定电流控制的LED驱动器芯片 一般说明 FP7122是在恒定关闭时间模式下工作的平均电流模式控制LED驱动器IC。FP7122不产生峰值到平均的误差&#xff0c;因此大大提高了LED电流的精度、线路和负载调节&#xff0c;而不需要任何回路补偿或高侧电流传感。输出的…

VB:二分法查找

VB&#xff1a;二分法查找 二分查找算法 Private Sub Command1_Click()Dim i%, m%, n%Dim x(1 To 10) As SingleFor i 1 To 10x(i) Val(InputBox("请输入"))Next iCall bubbleSort(x)For i LBound(x) To UBound(x) LBound(x)和UBound(x)是用于获取数组x的下界和上…

运营技巧|如何在不同的平台上高效批量管理账户?

在当今全球化时代&#xff0c;中国出海企业和B2B外贸企业越来越重视海外社媒营销&#xff0c;这已成为企业抢占市场份额的关键。并且&#xff0c;为了获取到更多流量&#xff0c;跨境人们还会开通Facebook、Twitter、Google、TikTok、Instagram等平台账号&#xff0c;搭建自己的…

教你制作作业查询系统

嗨&#xff0c;各位老师们&#xff0c;今天我要给你们介绍一个超级方便的工具——易查分&#xff01;你知道吗&#xff0c;利用易查分&#xff0c;我们可以轻松制作一个便捷高效的作业查询系统哦&#xff01; 是不是想有个自己的分班or成绩查询页面&#xff1f;博主给老师们争取…

提醒一个xampp启动mysql创建函数存在的坑

一直以来本地搭建的项目为了方便我都是使用xampp作为mysql的管理工具&#xff0c;比较简洁可视化比较好。但是最近程序的一个报错暴露了他与mysql之间的一些问题。 使用自增序列nextval函数时&#xff0c;突然抛出来一句&#xff1a; select nextval( SEQ_REGISTER_ID) > …

高性能数据JS网格 Bryntum Grid 5.5.2 Crack

高性能数据网格 Bryntum Grid 是一个高性能的网络表格组件。它是用纯 JavaScript 构建的&#xff0c;并且可以轻松地与所有主要 JS 框架集成。 功能丰富 Bryntum Grid 具有您期望从专业网格组件获得的所有功能&#xff0c;包括&#xff1a; 很好的表现;很好的绩效 没有人喜欢缓…

演讲实录:大模型时代,我们需要什么样的AI算力系统?

当前&#xff0c;“百模大战”带来了算力需求的爆发&#xff0c;AI芯片产业也迎来巨大机遇&#xff0c;“创新架构开源生态”正在激发多元AI算力产品百花齐放。面对新的产业机会&#xff0c;AI算力产业链亟需通过上下游协作共同把握机遇。 近日&#xff0c;浪潮信息AI&HPC…

异步FIFO设计的仿真与综合技术(1)

概述 本文主体翻译自C. E. Cummings and S. Design, “Simulation and Synthesis Techniques for Asynchronous FIFO Design 一文&#xff0c;添加了笔者的个人理解与注释&#xff0c;文中蓝色部分为笔者注或意译。 摘要&#xff08;ABSTRACT&#xff09; FIFO通常被用于将数据…