当前位置: 首页 > news >正文

线程池使用之自定义线程池

目录

一:Java内置线程池原理剖析

二:ThreadPoolExecutor参数详解

三:线程池工作流程总结示意图

四:自定义线程池-参数设计分析

1:核心线程数(corePoolSize)

2:任务队列长度(workQueue)

3:最大线程数(maximumPoolSize)

4:最大空闲时间(keepAliveTime)

五:自定义线程池-实现步骤

1:编写任务类(MyTask),实现Runnable接口;

2:编写线程类(MyWorker),用于执行任务,需要持有所有任务;

3:编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;

4:编写测试类(MyTest),创建线程池对象,提交多个任务测试;


一:Java内置线程池原理剖析

              我们要想自定义线程池,必须先了解线程池的工作原理,才能自己定义线程池;这里我们通过观察java中ThreadPoolExecutor的源码来学习线程池的原理

构造方法:
public ThreadPoolExecutor(int corePoolSize, //核心线程数量int maximumPoolSize,//     最大线程数long keepAliveTime, //       最大空闲时间TimeUnit unit,         //        时间单位BlockingQueue<Runnable> workQueue,   //   任务队列ThreadFactory threadFactory,    // 线程工厂RejectedExecutionHandler handler  //  饱和处理机制) 
{ ... }

二:ThreadPoolExecutor参数详解

我们可以通过下面的场景理解ThreadPoolExecutor中的各个参数;

a客户(任务)去银行(线程池)办理业务,但银行刚开始营业,窗口服务员还未就位(相当于线程池中初始线程数量为0),

于是经理(线程池管理者)就安排1号工作人员(创建1号线程执行任务)接待a客户(创建线程);

在a客户业务还没办完时,b客户(任务)又来了,于是经理(线程池管理者)就安排2号工作人员(创建2号线程执行任务)接待b客户(创建了一个新的线程);假设该银行总共就2个窗口(核心线程数量是2);

紧接着在a,b客户都没有结束的情况下c客户来了,于是经理(线程池管理者)就安排c客户先坐到银行大厅的座位上(空位相当于是任务队列)等候,

并告知他: 如果1、2号工作人员空出,c就可以前去办理业务;

此时d客户又到了银行,(工作人员都在忙,大厅座位也满了)于是经理赶紧安排临时工(新创建的线程)在大堂站着,手持pad设备给d客户办理业务;

假如前面的业务都没有结束的时候e客户又来了,此时正式工作人员都上了,临时工也上了,座位也满了(临时工加正式员工的总数量就是最大线程数),

于是经理只能按《超出银行最大接待能力处理办法》(饱和处理机制)拒接接待e客户;

最后,进来办业务的人少了,大厅的临时工空闲时间也超过了1小时(最大空闲时间),经理就会让这部分空闲的员工人下班.(销毁线程)

但是为了保证银行银行正常工作(有一个allowCoreThreadTimeout变量控制是否允许销毁核心线程,默认false),即使正式工闲着,也不得提前下班,所以1、2号工作人员继续待着(池内保持核心线程数量);

三:线程池工作流程总结示意图

四:自定义线程池-参数设计分析

             通过观察 Java 中的内置线程池参数讲解和线程池工作流程总结 , 我们不难发现 , 要设计一个好的线程池 , 就必须合理的设置线程池的 4 个参数 ; 那到底该如何合理的设计 4 个参数的值呢 ? 我们一起往下看 .
4 个参数的设计 :

1:核心线程数(corePoolSize)

  核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;

2:任务队列长度(workQueue)

  任务队列长度一般设计为:核心线程数/单个任务执行时间*2即可;例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1,则队列长度可以设计为200;

3:最大线程数(maximumPoolSize)

  最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000,那么,最大线程数=(最大任务数-任务队列长度)*单个任务执行时间;: 最大线程数=(1000-200)*0.1=80;

4:最大空闲时间(keepAliveTime)

  这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;

上面4个参数的设置只是一般的设计原则,并不是固定的,用户也可以根据实际情况灵活调整!

五:自定义线程池-实现步骤

1:编写任务类(MyTask),实现Runnable接口;

/*需求:自定义线程池练习,这是任务类,需要实现Runnable;包含任务编号,每一个任务执行时间设计为0.2秒*/
public class MyTask implements Runnable{private int id;//由于run方法是重写接口中的方法,因此id这个属性初始化可以利用构造方法完成public MyTask(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println("线程:"+name+" 即将执行任务:"+id);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程:"+name+" 完成了任务:"+id);}@Overridepublic String toString() {return "MyTask{" +"id=" + id +'}';}
}

2:编写线程类(MyWorker),用于执行任务,需要持有所有任务;

/*需求:编写一个线程类,需要继承Thread类,设计一个属性,用于保存线程的名字;设计一个集合,用于保存所有的任务;*/
public class MyWorker extends Thread{private String name;//保存线程的名字private List<Runnable> tasks;//利用构造方法,给成员变量赋值public MyWorker(String name, List<Runnable> tasks) {super(name);this.tasks = tasks;}@Overridepublic void run() {//判断集合中是否有任务,只要有,就一直执行任务while (tasks.size()>0){Runnable r = tasks.remove(0);r.run();}}
}

3:编写线程池类(MyThreadPool),包含提交任务,执行任务的能力;

/*这是自定义的线程池类;成员变量:1:任务队列   集合  需要控制线程安全问题2:当前线程数量3:核心线程数量4:最大线程数量5:任务队列的长度成员方法1:提交任务;将任务添加到集合中,需要判断是否超出了任务总长度2:执行任务;判断当前线程的数量,决定创建核心线程还是非核心线程*/
public class MyThreadPool {// 1:任务队列   集合  需要控制线程安全问题private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());//2:当前线程数量private int num;//3:核心线程数量private int corePoolSize;//4:最大线程数量private int maxSize;//5:任务队列的长度private int workSize;public MyThreadPool(int corePoolSize, int maxSize, int workSize) {this.corePoolSize = corePoolSize;this.maxSize = maxSize;this.workSize = workSize;}//1:提交任务;public void submit(Runnable r){//判断当前集合中任务的数量,是否超出了最大任务数量if(tasks.size()>=workSize){System.out.println("任务:"+r+"被丢弃了...");}else {tasks.add(r);//执行任务execTask(r);}}//2:执行任务;private void execTask(Runnable r) {//判断当前线程池中的线程总数量,是否超出了核心数,if(num < corePoolSize){new MyWorker("核心线程:"+num,tasks).start();num++;}else if(num < maxSize){new MyWorker("非核心线程:"+num,tasks).start();num++;}else {System.out.println("任务:"+r+" 被缓存了...");}}}

4:编写测试类(MyTest),创建线程池对象,提交多个任务测试;

/*测试类:1: 创建线程池类对象;2: 提交多个任务*/
public class MyTest {public static void main(String[] args) {//1:创建线程池类对象;MyThreadPool pool = new MyThreadPool(2,4,20);//2: 提交多个任务for (int i = 0; i <30 ; i++) {//3:创建任务对象,并提交给线程池MyTask my = new MyTask(i);pool.submit(my);}}
}

相关文章:

线程池使用之自定义线程池

目录 一&#xff1a;Java内置线程池原理剖析 二&#xff1a;ThreadPoolExecutor参数详解 三&#xff1a;线程池工作流程总结示意图 四&#xff1a;自定义线程池-参数设计分析 1:核心线程数(corePoolSize) 2:任务队列长度(workQueue) 3:最大线程数(maximumPoolSize) 4:最…...

Puppeteer无头浏览器:开启自动化之门,掌握浏览器世界的无限可能

大概还是入门期&#xff0c;我曾用Puppeteer做爬虫工具以此来绕过某网站的防爬机制。近期有需求要做任意链接网页截图&#xff0c;像这种场景非常适合用Puppeteer完成。无头浏览器我已知的还有Selenium。 完成截图需求踩的最大的坑不是具体的逻辑代码&#xff0c;而是Docker部…...

Ubuntu 23.10/24.04 LTS 放弃默认使用 snap 版 CUPS 打印堆栈

导读Canonical 的开发者、OpenPrinting 的项目负责人 Till Kamppeter 今年 5 月表示&#xff0c;计划在 Ubuntu 23.10&#xff08;Mantic Minotaur&#xff09;上默认使用 Snap 版本的 CUPS 打印堆栈。 不过经过数月的测试&#xff0c;官方放弃了这项决定。Ubuntu 23.10&#x…...

Linux CentOS7 history命令

linux查看历史命令可以使用history命令&#xff0c;该命令可以列出所有已键入的命令。 这个命令的作用可以让用户或其他有权限人员&#xff0c;进行审计&#xff0c;查看已录入的命令。 用户所键入的命令作为应保存的信息将记录在文件中&#xff0c;这个文件就是家目录中的一…...

XC5350A 单节锂电池保护芯片 过放2.9V/2.8V/2.4V保护IC

XC5350A产品是一个高集成度的鲤离子/聚合物电池保护解决方案。XC5350A包含先进的功率MOSFET&#xff0c;高精度电压检测电路和延迟电路XC5350A放入一个超小型SOT23-5封装&#xff0c;只有一个外部元件使其成为在电池组有限的空间的理想解决方案。 XC5350A具有包括过充&#xff…...

单片机论文参考:1、基于单片机的电子琴

摘要 随着社会的发展进步&#xff0c;音乐逐渐成为我们生活中很重要的一部分&#xff0c;有人曾说喜欢音乐的人不会向恶。我们都会抽空欣赏世界名曲&#xff0c;作为对精神的洗礼。本论文设计一个基于单片机的简易电子琴。电子琴是现代电子科技与音乐结合的产物&#xff0c;是一…...

Opencv源码解析(2)算法

目录 一&#xff0c;直方图均衡 1&#xff0c;直方图统计 2&#xff0c;灰度变换 3&#xff0c;直方图均衡 二&#xff0c;可分离滤波器 1&#xff0c;可分离滤波器的工厂 2&#xff0c;ocvSepFilter、sepFilter2D 3&#xff0c;Sobel 三&#xff0c;相位相关法 phase…...

让Mac菜单栏变得更加美观整洁——Bartender 5

Bartender 5是一款Mac电脑上的菜单栏图标管理软件&#xff0c;能够帮助您把菜单栏上的图标整理得更加美观、整洁和易于使用。如果您的菜单栏上充斥着许多图标&#xff0c;导致视觉上很不舒适和疲劳&#xff0c;那么Bartender 5就是解决这一问题的最佳选择&#xff01; Bartend…...

服务器迁移:无缝过渡指南

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

安卓开发中ViewBinding的使用

在安卓开发中&#xff0c;ViewBing 的作用就是简化 findViewById() 代码的写法。 看看下面的替换&#xff1a; etbinding.text //etfindViewById(R.id.text) 下面就看看怎么用的&#xff0c; 首先&#xff0c;打开app模块的build.gradle&#xff0c;然后添加如下代码&…...

【初阶数据结构】树(tree)的基本概念——C语言

目录 一、树&#xff08;tree&#xff09; 1.1树的概念及结构 1.2树的相关概念 1.3树的表示 1.4树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 二、二叉树的概念及结构 2.1二叉树的概念 2.2现实中真正的二叉树 2.3特殊的二叉树 2.4二叉树的性质…...

二叉树知识点

1.霍夫曼编码 这位作者写的很清楚 哈夫曼编码详解——图解真能看了秒懂_已知字符集abcdef,若各字符出现的次数_Young_IT的博客-CSDN博客 2.满二叉树与完全二叉树 满二叉树是指每层数量是pow(2,n-1)个节点&#xff0c;总节点数是pow(2,n)-1; 而完全二叉树是指最后一层不一定…...

Day69:283. 移动零、11. 盛最多水的容器、42. 接雨水

283. 移动零 leetcode链接&#xff1a;https://leetcode.cn/problems/move-zeroes/ 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。示例 1:…...

tensorrt的安装和使用

安装 提前安装好 CUDA 和 CUDNN&#xff0c;登录 NVIDIA 官方网站下载和主机 CUDA 版本适配的 TensorRT 压缩包即可。 以 CUDA 版本是 10.2 为例&#xff0c;选择适配 CUDA 10.2 的 tar 包&#xff0c;然后执行类似如下的命令安装并测试&#xff1a; #安装c版本 cd /the/pat…...

电压放大器在电子测试中的应用有哪些方面

电压放大器是一种常见的电子设备&#xff0c;广泛应用于各种测试和测量应用中。以下是电压放大器在电子测试中的几个主要方面应用的简要介绍。 信号采集与处理&#xff1a;电压放大器通常用于信号采集和处理&#xff0c;在测试过程中将低电平信号放大到适合进一步处理或分析的水…...

39.地址算术运算

如果p是一个指向数组中某个元素的指针&#xff0c;那么p将会对p进行自增运算并指向下一个元素&#xff0c;而pi将对p进行加i的增量运算&#xff0c;使其指向指针p当前所指向的元素之后的第i个元素。这类运算时指针或地址算术运算中最简单的形式。 allocbuf中的空间使用状况也是…...

没有外网的麒麟系统上搭建GitLab服务并且无需客户端账号密码验证

要在没有外网的麒麟系统上搭建GitLab服务并且无需客户端账号密码验证&#xff0c;可以按照以下步骤进行操作&#xff1a; 安装必要的依赖包和软件 sudo yum install curl policycoreutils-python openssh-server openssh-clients sudo systemctl enable sshd sudo systemctl …...

微服务生态系统:使用Spring Cloud构建分布式系统

文章目录 什么是微服务&#xff1f;为什么选择Spring Cloud&#xff1f;Spring Cloud的关键组件示例&#xff1a;构建一个简单的微服务步骤1&#xff1a;创建Spring Boot项目步骤2&#xff1a;配置Eureka服务发现步骤3&#xff1a;创建REST控制器步骤4&#xff1a;运行项目步骤…...

DIY 一个汽车方向盘游戏外设(MMOS OSW DIY)

OSW-MMOS直驱方向盘DIY过程记录 - 简书 (jianshu.com) DIY 一个汽车方向盘游戏外设&#xff08;MMOS OSW DIY&#xff09; 首先讲一下这个直驱系统大概的框架&#xff0c;首先是电脑&#xff0c;电脑里装MMOS的软件(这个软件国内高手把它汉化了的)&#xff0c;电脑通过USB线&a…...

校园网络技术需求分析

路由技术&#xff1a; 路由协议工作在 OSI 参考模型的第 3 层&#xff0c;因此它的作用主要是在通信 子网间路由数据包。路由器具有在网络中传递数据时选择最佳路径的能力。 除了可以完成主要的路由任务&#xff0c;利用访问控制列表&#xff08;Access Control List&#x…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...