【JavaEE】线程池
目录
前言
什么是线程池
线程池的优点
ThreadPollExecutor中的构造方法
corePoolSize && maximumPoolSize
keepAliveTime && unit
workQueue
threadFactory
如何在java中使用线程池
1.创建线程池对象
2.调用submit添加任务
3.调用shutdown关闭线程池
手动实现线程池
创建一个线程池类
测试
前言
在前面我们都是通过new Thread() 来创建线程的,虽然在java中对线程的创建、中断、销毁、等值等功能提供了支持,但从操作系统角度来看,频繁的创建和销毁线程,是需要大量的时间和资源的。那么我们能不能先把线程提前从系统中申请好,需要使用线程的时候,直接从这个地方取出来,而不是从系统中重新申请,等线程用完之后,也是放回到这个地方,而不用频繁的进行创建和销毁呢?那我们就需要用到线程池,线程池能够提高程序的效率。
什么是线程池
线程池 (ThreadPool) 是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建后保持活动的线程中执行这些任务。线程池的主要目的是减少创建和销毁线程的开销,提高响应速度和整体性能。
为什么说线程池里取线程,比从系统中创建一个线程效率要高呢?
我们从线程池里直接取出线程是纯用户态操作,而我们新建一个线程是内核态+用户态配合完成的。
内核态:指操作系统内核及其相关组件(如设备驱动程序)运行的状态。
用户态:指用户程序(如应用程序和服务)运行的状态。
操作系统=操作系统内核+操作系统配套的应用程序
假设现在要去银行办银行卡,那么银行内部人员就是内核态,前往办卡的就为用户态。
如果我们想要办卡,就走到小窗口向柜员说明情况,此时柜员就会帮你进行操作。但如果此过柜员问你要身份证复印件,如果没有,那么就需要去进行打印。
若请求柜员帮你进行打印,那么柜员什么时候打印,这个是不确定的,效率较低。这种就是用户态+内核态,调用系统API,由系统内核来完成这一系列操作。
若我们自己去自助复印机进行复印,整个过程就是连贯可控的,效率较高。这种就是纯用户态,由我们自己控制。
线程池的优点
- 减少资源消耗:通过重复利用已创建的线程,减少了创建和销毁线程所需的资源开销。
- 提高响应速度:当有新的任务到达时,若线程池中有空闲的线程,任务可以直接执行,无需等待新的线程创建。
- 防止资源耗尽:通过限制线程池中的最大线程数,可以避免因创建过多线程而引起的资源耗尽问题。
- 提高线程利用率:线程池中的线程可以被重复利用。
线程池最大的好处就是减少每次启动、销毁线程的损耗。
ThreadPollExecutor中的构造方法
在java中,给我们提供了现成的线程池供我们来使用。
我们可以看一下标准库中的线程池,在java.util.concurrent中给我们提供线程池的相关方法。
在点开concurrent之后,我们在接口中往下划,找到ThreadPoolExecutor类。我们可以看到,ThreadPoolExecutor类的构造方法有着好几个参数,那么这些参数都是什么意思呢?
‘’
我们拿第四个构造方法来看。
corePoolSize && maximumPoolSize
corePoolSize:即核心线程数
maximumPoolSize:最大核心线程数
线程池可以支持“线程扩容”,若某个线程池初始状态下有m个线程,但m若不够用,就会自动增加m的个数。
在java标准库的线程池中,把线程分为两类:核心线程和非核心线程。
核心线程可以理解为最少有多少个线程,而非核心线程就是线程扩容新增的线程。
核心线程数会始终存在线程池内部,而非核心线程,在繁忙的时候就会被创建出来,在空闲时就会把这些线程释放掉。
最大核心线程数=核心线程数+非核心线程数的最大值
keepAliveTime && unit
那么非核心线程在空闲的时候就会被立即释放掉吗?
若在空闲一会后,又有新的任务,那岂不是要重新新增线程。
所以在构造方法中,给我们提供了两个参数,用来设置非核心线程在空闲时等待任务的最长时间。
keepAliveTime:表示线程池中空闲的非核心线程在终止前等待新任务的最长时间。
unit:用来设置keepAliveTime参数的时间单位。
workQueue
我们可以看到workQueue的类型为BlockingQueue<Runnable>,说明这是个用来存放任务的阻塞队列。可以根据需要设置阻塞队列的类型,如果需要优先级,则可以使用PriorityBlockingQueue;如果不需要优先级且任务的数目是恒定的,则可以使用ArrayBlockingQueue;如果任务的数目不是恒定的,则可以使用LinkedBlockingQueue。
作用:
- 任务缓存:当线程池中的线程都在执行任务时,新提交的任务会被暂时存储在工作队列中,直到有空闲线程来执行它们。
- 任务调度:工作队列可以帮助线程池按照一定的策略来调度任务的执行顺序。
- 资源管理:通过限制工作队列的大小,可以控制线程池中等待执行的任务数量,从而避免资源耗尽的情况。
threadFactory
threadFactory:创建线程的⼯⼚,参与具体的创建线程⼯作.通过不同线程⼯⼚创建出的线程相当于 对⼀些属性进⾏了不同的初始化设置。
*handler(类型RejectedExecutionHandler)
最后一个参数是有关队列满后不要进行阻塞,而是要进行拒绝了,我们可以在构造方法上面查看线程池的拒绝策略有哪些.
在java提供了4种拒绝策略:
- ThreadPoolExecutor.AbortPolicy:有新的任务想要入队时,直接抛出异常
- ThreadPoolExecutor.CallerRunsPolicy:新添加的任务线程池拒绝执行,由添加任务的线程执行该任务
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃掉最旧的未被处理的请求,执行新的任务
- ThreadPoolExecutor.DiscardPolicy:丢弃掉当前新加的任务,不执行
讲完了线程池中的构造方法以及拒绝策略,那么如何在java中来使用线程池呢?
如何在java中使用线程池
1.创建线程池对象
ExecutorService ex=Executors.newFixedThreadPool(4);
ExecutorService是一个继承于Executor的接口,主要用来管理和控制线程,是Java并发编程的重要工具。
在java标准库中,虽然ThreadPoolExecutor功能强大,但是使用起来比较麻烦,所以java标准库对这个类进行了封装:Executors类
Executors类:是Java中用于创建线程池的工厂类,它提供了一系列的静态工厂方法,用于创建不同类型的线程池。
我们可以看到Executors工厂类中的工厂方法有以下几种类型:
- newCachedThreadPool():创建一个可缓存的线程池。这个线程池的线程数量可以根据需要自动扩展,如果有可用的空闲线程,就会重用它们;如果没有可用的线程,就会创建一个新线程。适用于执行大量的短期异步任务。
- newFixedThreadPool(int nThreads):创建一个固定大小的线程池,其中包含指定数量的线程。线程数量是固定的,不会自动扩展。适用于执行固定数量的长期任务。
- newSingleThreadExecutor():创建一个单线程的线程池。这个线程池中只包含一个线程,用于串行执行任务。适用于需要按顺序执行任务的场景。
- newScheduledThreadPool(int corePoolSize):创建一个固定大小的线程池,用于定时执行任务。线程数量固定,不会自动扩展此线程池;可以安排在给定延迟后运行命令或者定期执行。
- newSingleThreadScheduledExecutor():创建一个单线程的定时执行线程池。只包含一个线程,用于串行定时执行任务。
- newWorkStealingPool(int parallelism):创建一个工作窃取线程池,线程数量根据CPU核心数动态调整。这种线程池适合处理大量细粒度的可取消任务。
2.调用submit添加任务
这里我们创建的是一个固定线程数为4的线程池,在线程池中,我们想要添加任务需要调用submit()方法,可以看到需要的参数是一个Runnable类型的任务。
我们可以使用Lambda表达式或者直接new Runnable。(这里用第一个)
for(int i=0;i<100;i++) {int id = i;ex.submit(() -> {System.out.println(Thread.currentThread().getName() + " 任务:" + id);});}
当我们运行之后,会发现任务都结束后,程序还没有停止,这是为什么呢?
在 Java 中,线程池创建的线程默认为前台线程,虽然main线程结束了,但是线程池里的前台线程仍然存在。
3.调用shutdown关闭线程池
如果我们想要关闭线程池中的线程,我们可以使用shutdown方法。
public static void main(String[] args) {ExecutorService ex=Executors.newFixedThreadPool(4);for(int i=0;i<100;i++) {int id = i;ex.submit(() -> {System.out.println(Thread.currentThread().getName() + " 任务:" + id);});}ex.shutdown();}
既然我们学会如何使用java中的线程池,那么我们可以来实现一个线程池.
手动实现线程池
创建一个线程池类
由于这里是使用的阻塞队列,在队列满的时候若添加新的任务,会进入阻塞等待状态,不同于java线程池里的拒绝策略。
/*** 自定义线程池执行器类* 该类通过实现一个具有固定大小的线程池和一个阻塞队列来管理线程,用于异步执行任务*/
class MyThreadPoolExecutor {// 创建阻塞队列,用于存放待执行的任务// 队列大小设为1000,用于控制并发任务的数量,避免过多任务导致资源耗尽BlockingQueue<Runnable> blockingQueue=new ArrayBlockingQueue<>(1000);/*** 构造函数,初始化线程池* 创建一个线程,该线程循环从阻塞队列中取任务并执行* 这个线程是线程池中的工作线程,负责执行提交的任务*/public MyThreadPoolExecutor(int n) {for (int i = 1; i <= n; i++) {Thread t = new Thread(() -> {// 无限循环,确保线程池可以持续处理任务,直到程序中断或阻塞队列被清空while (true) {try {// 从阻塞队列中取出一个任务,如果队列为空,则线程被阻塞,直到有任务放入队列Runnable task = blockingQueue.take();// 执行取出的任务task.run();} catch (InterruptedException e) {// 如果线程在等待状态时被中断,抛出运行时异常// 这通常会导致程序异常终止throw new RuntimeException(e);}}});// 启动线程池中的工作线程t.start();}}/*** 提交一个任务到线程池* @param task 需要被执行的任务* 任务被放入阻塞队列中,随后由线程池中的工作线程执行*/public void submit(Runnable task){// 将任务放入阻塞队列,如果队列已满,则操作会阻塞,直到有空间可用blockingQueue.offer(task);}
}
测试
class DemoTest1{public static void main(String[] args) throws InterruptedException {MyThreadPoolExecutor ex=new MyThreadPoolExecutor(4);for(int i=0;i<100;i++) {int id = i;ex.submit(()->{System.out.println(Thread.currentThread().getName()+" 任务:"+id);});}}
}
本篇就先到这了~
若有不足,欢迎指正~
相关文章:

【JavaEE】线程池
目录 前言 什么是线程池 线程池的优点 ThreadPollExecutor中的构造方法 corePoolSize && maximumPoolSize keepAliveTime && unit workQueue threadFactory 如何在java中使用线程池 1.创建线程池对象 2.调用submit添加任务 3.调用shutdown关闭线程池…...

lvs实战项目-dr模式实现
一、环境准备 主机名IP地址router eth0:172.25.254.100 eth1:192.168.0.100 clienteth0:172.25.254.200lvseth1:192.168.0.50web1web2 1、client配置 [rootclient ~]# cat /etc/NetworkManager/system-connections/eth0.nmconne…...

JSONP跨域
1 概述 定义 json存在的意义: 不同类型的语言,都能识别json JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 s…...

Linux--shell脚本语言—/—终章
一、shell函数 1、shell函数定义格式 参数说明: 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果ÿ…...

免费代理池是什么,如何使用代理IP进行网络爬虫?
互联网是一个庞大的数据集合体,网络信息资源丰富且繁杂,想要从中找到自己需要的信息要花费较多的时间。为了解决这个问题,网络爬虫技术应运而生,它的主要作用就是在海量的互联网信息中进行爬取,抓取有效信息并存储。然…...

CAN直接网络管理(20240805)
长安CAN网络管理规范 个人理解:管理CAN网络中各NM节点的工作模式(状态); 1.术语定义 👉节点地址:用于唯一标识网络中每个节点的单字节数字,取值范围是 0x00~0xFF。👉状态迁移&#x…...

HTML5+CSS3笔记(Xmind格式):第二天
Xmind鸟瞰图: 简单文字总结: 新增选择器: 1.选择相邻兄弟 2.属性选择器 3.结构性伪类选择器 4.整体结构类型 5.标签结构类型 6.指定子元素的序号 7.文本选择伪元素 8.表单中使用的状态伪类选择器 9.内容…...

视频压缩文件太大了怎么缩小?6个视频压缩技巧,速度收藏起来!
高清视频文件,尤其是那些以 1080p 和 720p 清晰度为特征的视频,通常都拥有相当大的体积,会占据大量计算机存储空间。因此,为了更好地将它们进行分享和存储,您可能需要对它们进行压缩,以减小它们的尺寸。然而…...

Python接口自动化测试数据提取分析:Jmespath
1、引言 在处理JSON数据时,我们常常需要提取、筛选或者变换数据。手动编写这些操作的代码不仅繁琐,而且容易出错。Python作为一个功能强大的编程语言,拥有丰富的库和工具来处理这些数据。今天,将介绍一个实用的Python库——JMESP…...
特种设备作业叉车司机题库及答案
1.在我们平时工作中,经常接触的汽油、柴油、机油、油棉纱、木材等均为() A、助燃物质 B、可燃物质 C、着火源 参考答案:B 2.叉车满载行驶时,如合成重心靠后() A、有利于纵向稳定 B、有利于横向稳定 C、纵向和横向均有利 参考答案:A 3.蓄电池车行驶中放…...

Linux 操作系统速通
一、安装虚拟机 1. VmWare 安装下载 vmware workstation pro 16 下载 win R 输入 ncpa.cpl 确保网卡正常 2. CentOS 系统下载 CentOS 系统下载 将 CentOS 系统安装到虚拟机 3. 查看虚拟机 IP 命令 ifconfig 4. finalShell 安装下载 finalShell 下载 输入用户名一般是 ro…...

IIS漏洞大全(附修复方法)
IIS6.0 IlS Server 在 Web 服务扩展中开启了 WebDAV,配置了可以写入的权限,造成任意文件上传。 漏洞复现 fofa:"llS-6.0" or 本地搭建2003 server 1)开启 WebDAV 和写权限: 做好准备工作后开启环境,然后我们去访问配置的IP&#…...

HarmonyOS笔记3:从网络数据接口API获取数据
面向HarmonyOS的移动应用一般采用MVVM模式(见参考文献【1】),其中: M(Model层):模型层,存储数据和相关逻辑的模型。它表示组件或其他相关业务逻辑之间传输的数据。Model是对原始数据的进一步处理…...
Mac 下生成core dump
mac下生成core dump 使用ulimit -c查看ulimit设置,显示unlimited表示开启,显示0表示关闭,通过ulimit -c unlimited打开设置; 但是这个只在当前窗口有效果。如果需要变成系统全局设置。 就需要去改/etc/profile文件,打开,然后加上ulimit -c unlimited就可…...
详解Xilinx FPGA高速串行收发器GTX/GTP(1)--SerDes和GTX的关系
目录 1、SerDes和GTX的关系 2、传输总线的变化 2.1、从串行到并行 2.2、从并行又回到串行 文章总目录点这里:《FPGA接口与协议》专栏的说明与导航 1、SerDes和GTX的关系 Hold On,这个系列文章不是讲GTX收发器的吗?怎么一开始就扯到SerDes上了?GTX和SerDes之间有…...

golang实现Digest认证鉴权接口
什么是Digest认证鉴权接口? Digest认证鉴权接口是一种基于摘要算法的身份验证方法,用于确保API请求的安全性。在实际应用中,常常使用HTTP协议的Digest认证鉴权接口来验证请求的合法性。下面是一种常见的Digest认证鉴权流程: 1. 客户端发送HTTP请求到服务器,请求接口资源…...

机房托管服务器说明
机房托管服务器是指将企业或个人的服务器放置到专业数据中心(IDC机房)进行管理和维护,由数据中心提供稳定、安全的运行环境以及网络连接等基础设施支持。rak小编为您整理发布机房托管服务器说明详细内容。 通过托管服务器到专业机房,企业能够享受到高性能…...

CookieMaker工作室合作开发C++项目十一:拟态病毒
(注:本文章使用了“无标题技术”) 一天,我和几个同事,平台出了点BUG,居然给我刷出了千年杀,同事看得瑕疵欲裂,发誓要将我挫骨扬灰—— (游戏入口:和平精英31.…...
57、PHP 实现 从扑克牌中随机抽取5张牌,判断是不是一个顺子
题目: PHP 实现 从扑克牌中随机抽取5张牌,判断是不是一个顺子 描述: 即这5张牌是不是连续的2-10位数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。 解题思路…...

前端HTML+CSS查漏补缺——仿制百度搜索首页的一些思考
在像素模仿百度搜索首页的时候,在实现的时候,遇到了一些值得记录的点。 在这个过程中,也顺便看了看百度的源码,感觉很有意思。 对了,QQ截屏里面获取到的颜色,是不大正确的,会有点误差。 这是我…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...