线程池概念的详解

  前言👀~

上一章我们介绍了什么是定时器以及如何去实现一个定时器,今天我们来讲解在多线程中同样很重要的一个内容线程池

线程池的出现

线程池概念

标准库中的线程池

工厂模式

newCacheThreadPool方法

newFixedThreadPool方法

ThreadPoolExecutor类

ThreadPoolExecutor的构造方法(重要)

线程数相关参数的解释

关于线程池线程数目设置(重要)

时间参数的解释

阻塞队列参数的解释

工厂类参数的解释

线程池的拒绝策略参数的解释(重要)

手动模拟实现线程池


如果各位对文章的内容感兴趣的话,请点点小赞,关注一手不迷路,讲解的内容我会搭配我的理解用我自己的话去解释如果有什么问题的话,欢迎各位评论纠正 🤞🤞🤞

12b46cd836b7495695ce3560ea45749c.jpeg

个人主页:N_0050-CSDN博客

相关专栏:java SE_N_0050的博客-CSDN博客  java数据结构_N_0050的博客-CSDN博客  java EE_N_0050的博客-CSDN博客


线程池的出现

我们知道线程诞生的意义,是因为进程的创建和销毁的操作开销太大并且效率不高,但是呢如果频繁的创建和销毁线程,开销也不小

下面使用两种办法,进一步提高线程效率

1.协程(轻量级线程)它比线程更轻,它把系统调度的过程省略,我们自己动手调度,当下流行的并发编程的手段,在java中不够流行,GO和Python使用的更多,java只能靠第三方库去使用协程,但是第三方库不一定靠谱

2.线程池:咋们使用它来提高效率,池这个词是计算机中一种比较重要的概念,很多地方涉及到,例如线程池、进程池、内存池、连接池


线程池概念

线程池:一种并发编程中常用的技术,用于管理和重用线程。它由线程池管理器(ThreadPoolExecutor)、工作队列(BlockingQueue)和线程池线程(Thread)组成。在我们使用一个线程的时候,提前把后面的线程创建好放在线程管理器中,这样接下来要使用这些线程的时候,直接从线程池管理器中取即可,并且从工作队列中取出任务,让线程池中的线程去执行任务。线程池最大的好处是为了避免频繁地创建和销毁线程的开销,以及控制并发执行的线程数量,从而提高系统的性能和资源利用率

简易解释:在应用程序启动时创建一定数量的线程,并将它们保存在线程池中。当需要执行任务时,从线程池中获取一个空闲的线程,将任务分配给该线程执行。当任务执行完毕后,线程将返回到线程池,可以被其他任务复用

线程池可以看作是海王的鱼塘,即使有女朋友的情况下,还会看一个喜欢一个并且直接出手,全是它鱼塘里的鱼,即使分手了,从鱼塘里取条鱼直接衔接上,此时效率嘎嘎高,体验拉满


为什么从线程池中取线程比创建线程的效率高?

因为从线程池中取线程,这个操作属于是完全用户态的操作

创建线程这个操作是属于用户态+内核态配合完成的操作,就像我们之前写的代码,创建一个线程都是通过系统调用API去创建线程的,之前又说过操作系统中包含一个重要的模块内核,其实就是通过内核去进行这一系列的操作

例子说明为什么是用户态+内核态配合完成的操作:比如说我们要去办理营业执照,身份证肯定要带上的吧以及其他的证明,然后你去大厅找柜台的人给你办理营业执照,这时候人家说需要什么什么的,但是还差个身份证复印件,你没有,人家跟你说要么你去大厅那个复印机打印,要么我来帮你打印。你自己去打印的过程这个过程就是从线程池中取线程,这个操作属于是完全用户态的操作,因为是你自己去打印的,打印完立马就回来接着办理。你让人家给你打印这里会涉及到内核态,人家不知道去哪给你打印,也不知道这个期间它会不会干其他事,还要等。就像是操作系统是给所有进程提供服务的,当你要创建线程的时候,它会帮你去创建但是呢它可能还会去干一些其他的事情,不可控。所以可以这样想你自己去打印机复印就相当于直接从线程池取线程,去柜台找人复印就相当于内核帮你去创建线程。


标准库中的线程池

线程池对象因为是接口所有不能直接创建,需要通过一个专门的方法,返回一个线程池对象,后面有详细讲解

        ExecutorService service = Executors.newFixedThreadPool(3);

工厂模式

我们创建一个对象是通过new关键字去实现的,new这个关键字又会触发构造方法从而实现。但是构造方法存在一定的局限性,什么局限呢?我们要创建多个构造方法的时候,会受到重载的约束,参数类型要不同或者参数个数不同

这时候我们可以通过工厂模式解决这个问题单独搞一个类也就是工厂类,然后在这个类中搞些静态方法。通过一个static修饰的方法去代替构造方法完成初始化,只要把static修饰的方法名换一下,又可以代替一个不同的构造方法进行初始化,这样就不受重载的约束。由这样的静态方法负责构造出对象


newCacheThreadPool方法

这个newCacheThreadPool方法,看这英文就可以读出创建一个缓存线程池对象,缓存这玩意之前就说过,这里缓存的意思就是对于用过的线程我们不着急销毁,先保留一段时间留着下次用。然后创建出这个对象,有个特点能动态适应,什么意思呢?就是根据你的任务自动给你创建出线程,就比如说你的任务涉及到三个线程要工作,线程池对象会根据你的任务所需,自动创建出线程,并且像前面说的创建出来不着急销毁,先保留一段时间,以备后续再使用

newFixedThreadPool方法

创建出线程数量固定的线程池


 

还有很多能创建出线程池对象的方法根据需求选择使用对应的线程池


ThreadPoolExecutor类

上面的工厂方法生成的线程池本质上都是对一个ThreadPoolExecutor类进行封装
,标准库的几个工厂方法就是给这个类提供了不同参数的构造方法来创建线程池

ThreadPoolExecutor这个类的核心就两个,一个构造线程池,然后在里面有submit方法接收用户传入的任务然后进行处理

public class Test {public static void main(String[] args) {//ExecutorService这是一个接口不能实例化 用到工厂模式 通过Executors类的方法去创建线程池对象ThreadPoolExecutor这个类又包含线程池的重要属性//Executors这是一个类 这个方法能创建包含n个线程(不能说n是动态的)的线程池 返回值类型为 ExecutorServiceExecutorService service = Executors.newCachedThreadPool();//通过 ExecutorService.submit 可以注册一个任务到线程池中service.submit(new Runnable() {//通过这个方法 把你要执行的任务添加到线程池中@Overridepublic void run() {System.out.println("我是线程池对象");}});}
}

可以看到创建线程池对象返回的是一个ExecutorService类型,这是一个接口,下面我把源码展示给你们看,就知道为什么了

ThreadPoolExecutor的构造方法(重要)

ThreadPoolExecutor这个类的构造方法,最后一个构造方法涵盖所有的参数,我们围绕这个构造方法进行讲解,你也可以去官方文档看 https://docs.oracle.com/javase/8/docs/api/


线程数相关参数的解释

都是描述了线程的数目,corePoolSize是线程核心数目,maximmSize是线程最大数目,前面说了线程池里的线程数目是可以动态变化的,变化范围就是[线程核心数,线程最大数]

例子:可以把线程池理解为一个公司,我们都是公司的线程,但是有区别,正式员工是核心线程,有劳动法不会随便就被开除,实习生呢就是临时线程,随时都会被开除。正式员工的数目就是核心线程的数目,最大线程数目就是正式员工+实习生这样设置就能避免公司忙不过来,并且开销也减少了,专业点说就是满足效率的同时降低了系统开销

补充:核心线程(正式员工)不会消失,然后如果丢进去的任务认为超过了核心线程数,他就会创造临时线程数(实习生),然后临时的用完等待一会就消失了

关于线程池线程数目设置(重要)

有个问题我们在使用线程池,线程数目设置多少比较合适?只要你说了一个具体数字,就是错的,因为没接触过项目代码之前我们无法进行确认

设cpu核心数(逻辑核心数)是N

一个线程执行的代码主要有以下两类:

1.cpu密集型:代码里主要的逻辑是进行算术运算和逻辑判断

如果一个线程执行的代码都是cpu密集型,那线程池的线程数量设置等于N的时候已经是极限了,所以不超过N。因为超过N也没有多的核心数来处理这些线程了,此时通过操作系统的调度执行这些线程到cpu上工作反而会降低效率,增加开销

2.IO密集型:代码里主要进行的是IO操作,读写硬盘、网络通信等操作

如果一个线程执行的代码都是IO密集型,这个时候不吃CPU,此时设置线程池的线程数量可以超过N,因为上面说的cpu会调度执行这些线程也就是并发执行

实际项目代码中不可能全是执行一类的代码,而是两类都有,所以正确的做法是对程序进行测试,根据设置不同的线程数目来判断哪个线程数目下程序的综合性更好,或者根据要求来选择

时间参数的解释

keepAliveTime就是描述你作为实习生可以在公司摸鱼的时间,unit就是描述你摸鱼时间的单位(ms,s,min)。专业点说数值加单位组成时间。这两个参数什么意思呢?就是公司的任务少了持续了好一段时间为了节省开销就把你裁掉了,专业点说就是当线程数大于corePoolSize时,多余的临时线程能等待新任务的最长时间。只有当线程池中的线程数超过核心线程数时,这个参数才会生效

阻塞队列参数的解释

阻塞队列存放任务的也就是submit方法接收到的任务,存放到线程池中去进行处理,所以线程池中的任务是通过这个阻塞队列进行存储的。我们手动进行设置,也就是可以根据场景需求选择合适的队列,如果优先级优先采用PriorityBlockingQueue,如果任务数量比较固定采用ArrayBlockingQueue如果任务数量不固定变化大用LinkedBlockingQueue

工厂类参数的解释

就是使用这个工厂类去创建线程,能省去手动设置线程的一些属性(例如name、守护线程等),直接用工厂方法进行封装。省的你自己创建线程设置那些属性

线程池的拒绝策略参数的解释(重要)

首先要知道线程池中能存放的任务是有限的,如果超出这个数量,继续添加会出现什么效果,就是由这个拒绝策略决定的不同拒绝策略所展现的效果不一样,根据场景需求去选择对应的策略,下面有讲解,稍微会点英语就能知道什么意思了


ThreadPoolExecutor.AbortPolicy:这英文单词AbortPolicy的意思就是抛弃策略,超出能存放的任务数量,直接抛出异RejectedExecutionException

ThreadPoolExecutor.CallerRunsPolicy:这英文单词CallerRunsPolicy的意思就是调用者执行策略,超出能存放的任务数量,让添加新任务的这个线程执行

ThreadPoolExecutor.DiscardOldestPolicy:这英文单词DiscardOldestPolicy的意思就是丢掉最老策略,超出能存放的任务数量,丢掉队列中最老的任务

ThreadPoolExecutor.DiscardPolicy:这英文单词DiscardOldestPolicy的意思就是丢掉策略,超出能存放的任务数量,直接丢掉新添加的任务


手动模拟实现线程池

首先我们知道标准库线程池中有啥,有处理任务的线程,以及接收任务的submit方法,以及阻塞队列存放任务,下面是代码实现,简单模仿创建固定线程数目的线程池比较

class MyThreadPool {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);//阻塞队列存储我们的任务public MyThreadPool(int n) {for (int i = 0; i < n; i++) {Thread thread = new Thread(() -> {try {Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}}public void submit(Runnable runnable) throws InterruptedException {//这里采用的拒绝策略 阻塞等待queue.put(runnable);}}

以上便是本章线程池的内容,内容不少好好消化,我们下一章再见💕

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

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

相关文章

Akamai+Noname强强联合 | API安全再加强

最近&#xff0c;Akamai正式完成了对Noname Security的收购。本文我们将向大家介绍&#xff0c;经过本次收购后&#xff0c;Akamai在保护API安全性方面的后续计划和未来愿景。 Noname Security是市场上领先的API安全供应商之一&#xff0c;此次收购将让Akamai能更好地满足日益增…

图像基础知识

图像卷积 卷积(convolution)是通过两个函数f和g生成第三个函数的一种数学算子,表征函数f与g经过翻转和平移的重叠部分的面积。 卷积概念是两个变量在某范围内相乘后求和的结果。图像处理中的卷积概念:数字图像是一个二维的离散信号,对数字图像做卷积操作其实就是利用卷积…

Java进阶学习|Day4.Java多线程,线程池

文章目录 了解多线程CPU进程(Process)线程多线程开发多线程优点 实现方式继承Thread类实现Runnable接口实现Callable接口 线程状态转换线程状态线程调度调整线程优先级线程睡眠线程等待线程让步线程加入线程唤醒 线程同步线程同步方式多线程间通信 线程池了解线程池定义常见接口…

可视化作品集(02):应急预警上的应用

应急预警领域是可视化大屏的一个重要应用场景&#xff0c;大屏展示的海量数据能为应急工作提供数据支持&#xff0c;本文带大家看看这类大屏的设计。 可视化大屏在应急和预警领域有广泛的应用&#xff0c; 1. 突发事件监测和应急响应&#xff1a; 可视化大屏可以实时展示突发…

顺序表的应用——通讯录的实现

前言 本篇博客将接着上次顺序表的内容进行拓展应用&#xff0c;这次来为大家介绍通讯录的实现&#xff0c;它就是基于顺序表的结构完成的&#xff1b;如果你对此感兴趣&#xff0c;请看下面的内容&#xff1b; 1.顺序表的应用 我们前面学过&#xff0c;顺序表可以存放任意类…

Java | Leetcode Java题解之第214题最短回文串

题目&#xff1a; 题解&#xff1a; class Solution {public String shortestPalindrome(String s) {int n s.length();int[] fail new int[n];Arrays.fill(fail, -1);for (int i 1; i < n; i) {int j fail[i - 1];while (j ! -1 && s.charAt(j 1) ! s.charAt…

ASP.NET Core Blazor 5:Blazor表单和数据

本章将描述 Blazor 为处理 HTML 表单提供的特性&#xff0c;包括对数据验证的支持。 1 准备工作 继续使用上一章项目。   创建 Blazor/Forms 文件夹并添加一个名为 EmptyLayout.razor 的 Razor 组件。本章使用这个组件作为主要的布局。 inherits LayoutComponentBase<div …

论文 | PRCA: 通过可插拔奖励驱动的上下文适配器拟合用于检索问答的黑盒大语言模型

论文全称&#xff1a;PRCA: Fitting Black-Box Large Language Models for Retrieval Question Answering via Pluggable Reward-Driven Contextual Adapter 核心问题&#xff1a;如何在检索增强式问答&#xff08;ReQA&#xff09;任务中&#xff0c;利用大型语言模型&#xf…

【C语言入门】初识C语言:掌握编程的基石

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C语言 “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C语言入门 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀C语言入门 &#x1f4d2;1. 选择…

单片机关键任务优先级的实现学习

与总体产品联调时&#xff0c;需要各个单机系统严格按照总体要求&#xff0c;进行数据输出&#xff0c;时间的偏差将出现系统异常&#xff0c;控制失败等不稳定情况产生&#xff0c;甚至影响到产品安全。 因此必须确保某些关键任务的优先执行。单片机任务优先级一般有两种方式…

[小试牛刀-习题练]《计算机组成原理》之指令系统

一、选择题 0.【指令-课本习题】某计算机按字节编址&#xff0c;指令字长固定且只有两种指令格式&#xff0c;其中三地址指令29条&#xff0c;二地址指令107条&#xff0c;每个地址字段为6位&#xff0c;则指令字长至少应该是&#xff08;A&#xff09; A.24位 B. 26位 C. 28位…

ctfshow web sql注入 web242--web249

web242 into outfile 的使用 SELECT ... INTO OUTFILE file_name[CHARACTER SET charset_name][export_options]export_options:[{FIELDS | COLUMNS}[TERMINATED BY string]//分隔符[[OPTIONALLY] ENCLOSED BY char][ESCAPED BY char]][LINES[STARTING BY string][TERMINATED…

Android系统层屏蔽弹出停止运行对话框

项目场景&#xff1a; 车载项目&#xff0c;ATC8257-Android9.0系统平台&#xff0c;福田汽车P3系列项目 项目使用高德公版地图前提是无法获得任何高德定制服务&#xff0c;每次刷完机去切换语言系统会弹出"高德地图已停止运行"弹窗&#xff0c;严重影响用户使用体…

【第三版 系统集成项目管理工程师】第6章 数据工程

持续更新。。。。。。。。。。。。。。。 【第三版】第六章 数据工程 6.1数据采集和预处理6.1.1 数据采集 P2346.1.2 数据预处理6.1.3 数据预处理方法1.缺失数据的预处理-P2352.异常数据的预处理-P2363.不一致数据的预处理-P2364.重复数据的预处理-P2365.格式不符数据的预处理…

UE5 03-物体碰撞检测

在你需要碰撞的物体上添加一个碰撞检测组件 碰撞预设 设置为NoCollision,这样移动过程中就不会有物理碰撞阻挡效果,只负责检测是否碰撞,比较难解释,如果学过Unity的话,可以把它理解成 Collision 为 Trigger

My sql 安装,环境搭建

以下以MySQL 8.0.36为例。 一、下载软件 1.下载地址官网&#xff1a;https://www.mysql.com 2. 打开官网&#xff0c;点击DOWNLOADS 然后&#xff0c;点击 MySQL Community(GPL) Downloads 3. 点击 MySQL Community Server 4.点击Archives选择合适版本 5.选择后下载第二个…

密码学复习

目录 基础 欧拉函数 欧拉函数φ(n)定义 计算方法的技巧 当a=a_1*a_2*……*a_n时 欧拉定理 剩余系 一些超简单密码 维吉尼亚 密钥fox 凯撒(直接偏移) 凯特巴氏(颠倒字母表) 摩斯密码(字母对应电荷线) 希尔(hill)密码 一些攻击 RSA 求uf+vg=1 快速幂模m^…

Python | Leetcode Python题解之第213题打家劫舍II

题目&#xff1a; 题解&#xff1a; class Solution:def rob(self, nums: List[int]) -> int:def robRange(start: int, end: int) -> int:first nums[start]second max(nums[start], nums[start 1])for i in range(start 2, end 1):first, second second, max(fi…

Bootstrap 图片

Bootstrap 图片 Bootstrap 是一个流行的前端框架,它提供了一套丰富的工具和组件,用于快速开发响应式和移动优先的网页。在本文中,我们将探讨如何使用 Bootstrap 来处理和展示图片,包括图片的响应式设计、图片样式和图片布局。 响应式图片 Bootstrap 通过其栅格系统提供了…

人工智能在物流领域的应用,智慧物流大有可为!

物流是复合型服务产业&#xff0c;作为经济的重要组成部分&#xff0c;受到人工智能技术的深刻影响。物流行业的人工智能应用也将助推人工智能技术的发展&#xff0c;人工智能技术应用于物流行业&#xff0c;应用领域包括以下方向&#xff1a; 第一、车货匹配系统 使用人工智…