线程池【开发实践】
文章目录
- 一、为什么要用线程池
- 1.1 单线程的问题
- 1.2 手动创建多线程的问题
- 1.3 线程池的作用(优点)
- 1.4 线程池的使用场景
- 二、线程池的基础知识
- 2.1 线程池的核心组件
- 2.2 JUC中的线程池架构
- 2.3 线程池的配置参数
- 2.4 线程池常见的拒绝策略(可自定义)
- 2.5 线程池的生命周期
- 2.6 线程池的任务调度规则
- 2.7 线程池执行器的钩子方法
- 三、使用基础
- 3.1 常见的工作队列
- 3.2 Executors提供的线程池
- 3.3 使用Executors创建线程的问题
- 四、开发实践
- 4.1 线程数量配置
- 4.2 提交任务:以submit(callable)为例
- 4.3 关闭线程池
- 参考
一、为什么要用线程池
1.1 单线程的问题
单线程无法利用CPU的多核资源,且存在阻塞问题。使用单个线程来处理一组任务,若当前任务阻塞了线程,该线程将失去CPU的使用权,而不是去执行其他可以执行的任务。
1.2 手动创建多线程的问题
需要开发人员去管理线程的生命周期,麻烦,且可能出错。
还存在并发数不可控的问题,如每次到达一个任务时都新建一个线程来处理,当大量任务到达时,会创建大量的线程,导致线程间的竞争加剧,且可能导致系统资源耗尽。
1.3 线程池的作用(优点)
- 节省线程创建和销毁的开销:线程池通过复用一组线程,可以节省频繁创建和销毁线程的开销。
- 控制并发数:线程池可以控制最大并发数,避免线程过多导致系统资源耗尽。
- 提高响应速度:当任务到达时,可以直接使用线程池中已经创建好的线程,节省了新建线程的时间。
- 管理线程:线程池负责内部线程的生命周期管理,包括创建、销毁和调度等,无需人工干预。
1.4 线程池的使用场景
- 处理并发请求:每个请求都要一个线程来处理,使用线程池来复用一组线程,能节省创建和销毁线程的开销。
- 执行异步任务:避免耗时操作(如邮件发送、文件上传)阻塞主线程,可以使用线程池来异步执行这些任务。
- 处理IO密集型任务:线程在进行IO操作时会阻塞,使用线程池来调度这些任务,可以在任务阻塞时调度其他任务继续执行,避免CPU空闲。
- 处理计算密集型任务:使用线程池来执行计算密集型任务,可以充分利用CPU的多核资源,也能控制并发数量避免系统资源耗尽。
- 后台服务:利用线程池来执行后台服务,与前台线程的执行独立,互不影响。
- 执行定时任务与周期性任务:可以使用
ScheduledThreadPool在后台执行这些任务,与前台线程的执行独立。
二、线程池的基础知识
2.1 线程池的核心组件
- 线程池管理器:负责线程池的创建、销毁、管理和配置。
- 工作线程:工作线程是线程池中的实际工作者,负责执行提交给线程池的任务。
- 任务接口:每个提交到线程池的任务都需要实现一个任务接口。
Runnable用于定义无需返回值的任务,而Callable则用于定义可以有返回值的任务,并且可以抛出异常。 - 工作队列(任务队列):用于暂存等待被执行的任务,有多种实现方式,未必是先进先出。
- 线程工厂:线程工厂的作用是定义一个规范来创建线程,允许在创建线程时进行定制化设置,例如设置线程的名称、优先级、是否为守护线程(daemon thread)等属性。
- 拒绝策略:见后。
(后三个核心组件是线程池的后三个配置参数)
2.2 JUC中的线程池架构
Executor
是线程池管理器的顶层接口,只定义了一个方法void execute(Runnable command),用来提交Runnable任务。
ExecutorService
该接口扩展了Executor,增加了更多管理线程池的方法,如提交Callable任务、提交批量任务、线程池的生命周期管理(shutdown()和shutdownNow())、获取线程池状态等。
AbstractExecutorService实现了该接口中的部分方法,为创建自定义的线程池服务实现提供了基础框架。
ThreadPoolExecutor继承自AbstractExecutorService,是JUC中线程池管理器的核心实现类。
Executors是一个静态工厂类,提供了多种线程池管理器。
ScheduledExecutorService
该接口扩展了ExecutorService,增加了定时执行和周期性执行任务的功能。ScheduledThreadPoolExecutor实现该接口,专为计划任务而设计。

2.3 线程池的配置参数
- corePoolSize:核心线程数。
- maximumPoolSize:最大线程数。
- keepAliveTime:空闲线程存活时间。当线程池中的工作线程数量超过核心线程数时,额外的工作线程在空闲这个时间段后会被终止,直到工作线程数量降到核心线程数。但如果设置了
allowCoreThreadTimeOut为true,那么核心线程也可以被终止。 - unit:空闲线程存活时间的单位,可以用枚举类
TimeUnit设置。 - workQueue:工作队列。
- threadFactory:线程工厂。
- handler:拒绝策略。当阻塞队列已满且工作线程数大于等于最大线程数时,无法接受新的任务,会按照拒绝策略进行处理。
2.4 线程池常见的拒绝策略(可自定义)
- AbortPolicy:拒绝策略。是默认策略,会将新任务拒绝,并且抛出
RejectedExecutionException异常。 - DiscardPolicy:抛弃策略。会将新任务丢掉,但不会跑出异常。
- DiscardOldestPolicy:抛弃最老任务策略。不是丢弃新任务,而是先丢弃最先进入队列的任务,然后将新任务入队。
- CallerRunsPolicy:调用者执行策略。让提交任务的线程去执行新任务,而不是使用线程池中的线程去执行。

2.5 线程池的生命周期
- New:新建状态。当线程池创建后,但尚未开始执行任务时,它处于新建状态。此时,线程池中的线程尚未启动。
- Running:运行状态。新建的线程池开始接受并处理任务后就会进入运行状态,这是线程池的正常工作状态,可以接收新任务和处理工作队列中的任务。
- Shutdown:关闭状态。调用
shutdown()方法后,线程池会进入关闭状态。此时,线程池不再接受新任务,但不会立刻终止,它会继续处理队列中已有的任务直到所有任务完成。 - Stop:停止状态。调用
shutdownNow()方法后,线程池会进入停止状态,此时会中断正在执行任务的线程,清空任务队列,并返回未开始执行的任务列表。 - Tidying:整理状态。完成关闭状态或停止状态的工作后,线程池会进入整理状态。此阶段,线程池中的线程数量降为0,即将调用
terminated()钩子方法。 - Terminated:终止状态。在整理状态后,线程池调用
terminated()方法并进入终止状态。此时线程池生命周期结束,会释放所有资源。

2.6 线程池的任务调度规则
当向线程池提交一个任务时:
- 若线程池中的工作线程数量小于核心线程数,执行器总是优先创建一个新的工作线程来执行任务,而不是使用一个空闲的工作线程(目的是快速让线程池中有足够的活跃线程)。若工作线程数量大于等于核心线程数,则根据工作队列的情况进行相应的处理。
- 若工作队列未满,则将任务入队。若工作队列已满,则根据工作线程数量与最大线程数的关系进行相应的处理。
- 若工作线程数大于等于最大线程数,则执行拒绝策略。否则,会新建一个非核心线程来立即执行新的任务。

2.7 线程池执行器的钩子方法
beforeExecute(Thread t, Runnable r): 任务执行之前的钩子方法。
afterExecute(Runnable r, Throwable t): 任务执行之后的钩子方法。
terminated(): 线程池终止时的钩子方法。
三、使用基础
3.1 常见的工作队列
工作队列基于阻塞队列实现,我们可以实现BlockingQueue来自定义阻塞队列,也可以使用JUC中提供的阻塞队列:
- ArrayBlockingQueue:基于数组的有界阻塞队列。队列大小是固定的,在创建时必须指定。由于是基于数组,所以访问速度快,但可能有扩容受限的问题。
- LinkedBlockingQueue:基于链表的阻塞队列。如果不指定容量,则默认为Integer.MAX_VALUE。相比ArrayBlockingQueue,插入和删除操作可能稍微慢一点,但由于链表的动态性,它可以更灵活地调整大小。
- PriorityBlockingQueue:基于最小堆的无界的优先级队列,元素按照自然排序或提供的比较器进行排序。任务按照优先级顺序被处理,而非先进先出。
- DelayQueue:基于
PriorityBlockingQueue的无界阻塞队列,其中元素只有在延迟期满后才能被获取,否则将阻塞等待,常用于实现定时任务和延迟操作。 - SynchronousQueue:一个特殊的队列,它没有内部容量,每个插入操作必须等待另一个线程的对应移除操作,反之亦然。适用于直接的生产者-消费者传递,非常适合传递性操作。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列,支持公平和非公平模式。它还提供了一个
tryTransfer()方法,允许生产者直接将元素转移给等待中的消费者,如果没有消费者等待,则可以选择是否阻塞(生产者可阻塞)。 - LinkedBlockingDeque:双向链表实现的双向阻塞队列,既可以用作栈,也可以用作队列。它支持有限或无界的队列,并提供了
putFirst()、putLast()等方法来控制元素的插入位置。
3.2 Executors提供的线程池

3.3 使用Executors创建线程的问题
- 定长线程池/单线程话线程池:阻塞队列无界,可能导致JVM出现OOM(Out Of Memory)异常。
- 定时线程池/可缓存线程池:最大线程数量不设限上,如果任务提交较多,就会造成大量的线程被启动,可能造成OOM异常,也可能导致CPU线程资源耗尽。
四、开发实践
4.1 线程数量配置
IO密集型
核心线程数设置得比CPU核心数稍大,因为当线程在等待I/O时,CPU可以调度其他线程执行。最大线程数可以设置得更高,甚至远大于CPU核心数,具体数值取决于系统的I/O能力及预期的并发水平。一般推荐设置为2 * CPU核心数或更高,但需注意不要设置得过高以免过度消耗系统资源。
计算密集型
核心线程数和最大线程数通常设置为CPU核心数,因为在这种情况下,更多的线程并不能带来性能提升,反而会因为上下文切换带来额外开销。
4.2 提交任务:以submit(callable)为例
// 创建线程池ExecutorService executor = Executors.newSingleThreadExecutor();// 创建Callable任务对象Callable<Result> task = () -> {// ...};// 提交任务并获取Future对象Future<Result> future = executor.submit(task); // 提交任务并获取Future// 获取结果try {// get()方法会阻塞,如果任务执行过程中发生了异常,那么此处会抛出该异常Result res = future.get();} catch (InterruptedException e) {// 处理中断异常...} catch (ExecutionException e) {// 处理执行异常...}// 关闭ExecutorServiceexecutor.shutdown();
4.3 关闭线程池
调用shutdown()或shutdownNow()后,当前线程不会等待线程池的关闭,若当前线程结束了,可能导致内存泄露和资源浪费。
若线程池中还有活动的线程,就算主线程结束了,JVM也会因为存在活跃的非守护线程而无法退出,导致应用程序无法正常退出。
awaitTermination()方法用来阻塞当前线程,直到线程池中的所有任务完成执行或者超时,或者当前线程被中断。可以用于等待线程池中的线程全部执行完后再退出当前线程,线程池中的所有线程在超时前执行完毕,会返回true并恢复当前线程的执行。
ExecutorService executorService = Executors.newFixedThreadPool(10);// ... 提交任务到线程池// 关闭线程池
executorService.shutdown();
try {// 等待一定时间,直到超时或者线程池中的线程全部执行结束if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {// 如果超时,则尝试强制关闭executorService.shutdownNow();// 等待一定时间,直到超时或者线程池中的线程全部执行结束if (!executorService.awaitTermination(60, TimeUnit.SECONDS))System.err.println("线程池未在规定时间内停止!");}
} catch (InterruptedException ie) {// 强制终止线程池executorService.shutdownNow();// 保持当前线程的中断状态Thread.currentThread().interrupt();
}
参考
Java线程池(超详细)
Java 多线程:彻底搞懂线程池
相关文章:
线程池【开发实践】
文章目录 一、为什么要用线程池1.1 单线程的问题1.2 手动创建多线程的问题1.3 线程池的作用(优点)1.4 线程池的使用场景 二、线程池的基础知识2.1 线程池的核心组件2.2 JUC中的线程池架构2.3 线程池的配置参数2.4 线程池常见的拒绝策略(可自定…...
论文辅助笔记:ST-LLM
1 时间嵌入 2 PFA(Partial Frozen Architecture) 3 ST_LLM 3.1 初始化 3.2 forward...
加入运动健康数据开放平台,共赢鸿蒙未来
HarmonyOS SDK运动健康服务(Health Service Kit)是为华为生态应用打造的基于华为帐号和用户授权的运动健康数据开放平台。在获取用户授权后,开发者可以使用运动健康服务提供的开放能力获取运动健康数据,基于多种类型数据构建运动健…...
企业化运维(7)_Zabbix企业级监控平台
官网:Zabbix :: The Enterprise-Class Open Source Network Monitoring Solution ###1.Zabbix部署### (1)zabbix安装 安装源 修改安装路径为清华镜像 [rootserver1 zabbix]# cd /etc/yum.repos.d/ [rootserver1 yum.repos.d]# vim zabbix.r…...
CTF php RCE (一)
0x01 引言 首先进入题目 应该是大部分都是一段白盒PHP审计,然后我们为了命令执行,绕过或者是钻空子等等操作,来拿到flag 0x02 基础 0x01 传参方式 这里有两个工具,hackbar和burpsuite,这两个工具非常实用 大家可以自己Googl…...
Proteus + Keil单片机仿真教程(五)多位LED数码管的静态显示
Proteus + Keil单片机仿真教程(五)多位LED数码管 上一章节讲解了单个数码管的静态和动态显示,这一章节将对多个数码管的静态显示进行学习,本章节主要难点: 1.锁存器的理解和使用; 2.多个数码管的接线封装方式; 3.Proteus 快速接头的使用。 第一个多位数码管示例 元件…...
【Linux】网络新兵连
欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 引言 在上一篇博客中,我们简单的介绍了一些Linux网络一些比较基本的概念。本篇博客我们将开始正式学习Linux网络套接字的内容,那么我们开始吧! 1.网络中的地址管理 大家一…...
基于STM32的智能加湿器
1.简介 基于STM32的加湿器发展前景非常乐观,这主要得益于其在技术、市场需求、应用场景以及政策支持等多方面的优势。STM32微控制器具备强大的处理能力和丰富的外设接口,能够实现精确的湿度监测和智能化控制。基于STM32的加湿器可以根据环境湿度自动调节…...
ubuntu 如何解压tar
在Ubuntu中解压.tar文件,可以使用tar命令。以下是解压.tar文件的命令: tar -xvf file.tar 解释: x 表示解压 v 表示显示过程中的详细信息(可选) f 表示后面跟文件名 这将在当前目录下解压file.tar文件的内容。如果…...
C++ 算法——二分查找
如果要你在一个升序序列中查找一个值的位置,你是否还会傻乎乎的用下面这个 O ( n ) \mathcal O(n) O(n) 的代码暴力查找,如果是,我告诉你,其实根本不用这么做。 int find(int a[],int n,int k) {for(int i0;i<n;i) if(a[i]k)…...
【自动驾驶仿真在做什么——初学者总结(陆续补充)】
文章目录 基础概念自动驾驶级别再稍提一下ODD是什么? 自动驾驶仿真分类软件在环仿真硬件仿真 仿真究竟难在哪?关于lidar和radar区别一些名词解释 最近也是学习自动驾驶仿真相关知识,习惯去总结一下,方便自己回顾和总结,…...
探索HTML5的设计原则:引领Web开发的未来方向
随着互联网的飞速发展,HTML5作为Web技术的核心标准之一,不仅极大地丰富了网页的表现力和交互性,还推动了Web应用向更加动态、高效、安全的方向迈进。HTML5的设计原则,体现了对用户体验、内容可访问性、跨平台兼容性以及未来可扩展…...
力扣喜刷刷--day1
1.无重复字符的最长子串 知识点:滑动窗口 基本概念 窗口:窗口是一个连续的子序列,可以是固定长度或可变长度。滑动:窗口在数据序列上移动,可以是向左或向右。边界:窗口的起始和结束位置。 应用场景 字符…...
配置linux的yum镜像为阿里镜像源
1.备份当前的yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2.下载新的CentOS-Base.repo 到/etc/yum.repos.d wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo 3.清空并生成缓存 yum clean …...
react使用markdown进行展示
有一些文档非常长,但是又要挨个设置样式,直接用 组件库 - marked 注意文档要放在public下才能读取。但非常方便 import { marked, Renderer } from "marked".....const [html, setHtml] useState<any>("")const renderer:…...
实时温湿度监测系统:Micropython编码ESP32与DHT22模块的无线数据传输与PC端接收项目
实时温湿度监测系统 前言项目目的项目材料项目步骤模拟ESP32接线连接测试搭建PC端ESP32拷录环境对ESP32进行拷录PC端搭建桌面组件本地数据接收桌面小组件部分 实验总结 前言 人生苦短,我用Python。 由于我在日常工作中经常使用Python,因此在进行该项目…...
CloudWatch Logs Insights 详解
CloudWatch Logs Insights 是 AWS 提供的强大日志分析工具,允许您快速、交互式地搜索和分析日志数据。本文将详细介绍使用 CloudWatch Logs Insights 所需的权限、常用查询方法,以及一些实用的查询示例。 1. 所需权限 要使用 CloudWatch Logs Insights,用户需要具备以下 I…...
Jmeter在信息头中设置Bearer与 token 的拼接值
思路:先获取token,将token设置成全局变量,再与Bearer拼接。 第一步:使用提取器将token值提取出来,使用setProperty函数将提取的token值设置成全局变量,在登录请求后面添加BeanShell取样器 或者 BeanShell后…...
C#程序调用Sql Server存储过程异常处理:调用存储过程后不返回、不抛异常的解决方案
目录 一、代码解析: 二、解决方案 1、增加日志记录 2、异步操作 注意事项 3、增加超时机制 4、使用线程池 5、使用信号量或事件 6、监控数据库连接状态 在C#程序操作Sql Server数据库的实际应用中,若异常就会抛出异常,我们还能找到异…...
数据统计与数据分组18-25题(30 天 Pandas 挑战)
数据统计与数据分组 1. 知识点1.18 分箱与统计个数1.19 分组与求和统计1.20 分组获取最小值1.21 分组获取值个数1.22 分组与条件查询1.23 分组与条件查询及获取最大值1.24 分组及自定义函数1.25 分组lambda函数统计 2. 题目2.18 按分类统计薪水(数据统计)…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
