本篇会加入个人的所谓鱼式疯言
❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!
在当今这个多核处理器成为标配的时代,如何高效地利用计算资源已成为软件开发中不可忽视的关键因素。随着应用程序变得越来越复杂,对并发处理的需求也日益增长。试想一下,在一个繁忙的服务器上,如果每一个新任务都创建一个新的线程来处理,那么系统将不堪重负——频繁的线程创建和销毁不仅消耗大量的时间和内存,还可能导致系统性能急剧下降。
前言
为了解决这一问题,并实现 更高效的资源管理 ,线程池
的概念应运而生。线程池是一种用于 管理和复用一组预先创建好的线程 的方法,它通过预先创建一定数量的工作线程并将其 置于池中等待分配任务 ,从而 避免了频繁创建和销毁线程
带来的开销。
接下来的内容中,小编将带着小伙伴深入探讨 线程池的基本原理 、 参数类型
目录
-
线程池的初识
-
Java标准库中线程池的参数列表(重点)
一. 线程池的初识
1. 线程池的引入
我们知道,线程对于进程来说, 线程是 更微量的进程 ,开销更小, 但对于线程的本身来说虽然开销比较大。
但随着当前
并发编程的日益需求
越来越大, 频繁的从系统中 创建线程 ,销毁线程的开销
还是相对而已比较大的。
所以我们就引入 “线程池” 的概念, 具体线程池是什么,该怎么理解,下面让小编具体来说明下吧
2. 线程池的概念以及理解
<1>. 线程池的概念
池
的概念好比一个地方,这个地方操作系统提前创建 好多线程 放在这个地方。
当 任务繁忙 的时候,我们就
从这个地方中调用这些线程
。
当 任务不繁忙 的时候,我们就把这些
线程重新调度回这个地方
。
而这个地方我们就成为 “线程池”
。
<2>. 线程池的理解
我们知道, 线程是操作系统进行 调度执行的基本单位 。
每次创建线程是从操作系统中 创建并且调度执行的 。
操作系统可以简单 分为 内核区 和 用户区。
内核区
是操作系统硬件的内层管理
用户区
是各种软件资源的任务执行
而操作系统面对多个软件的运行事务是十分繁忙的, 每创建一个 线程 去调度执行任务也不可控的。
好比现在小编要去银行去办理取钱事务, 就想柜台提出诉求, 但柜台说需要户口本复印件。
这时银行这边刚好有打印机
我有两种方式可以得到户口本的复印件
-
请柜台小姐姐帮我打印一下
-
自己动手打印
如果我们操作系统创建线程的, 就相当于请柜台小姐姐帮我 打印户口本 ,但柜台小姐姐的事务是
比较繁忙的
, 这个执行是 不可控的 , 我们并不知道柜台小姐姐什么时候完成事务来帮我打印户口本。
所以就像我们的操作系统创建线程一样,由于我们的操作系统是繁忙的,就有可能对于创建线程的这种需求,造成 一定的开销。
而我们在线程池中去调度执行线程, 就好比上面的第二种方案,这是我们自己的去动手完成我们自己的需求,具体需要多久去完成,这些操作是由我们自己决定的, 这种行为是可控的。 所以 开销势必会少很多 , 也会
更轻量
。
二. Java标准库中线程池的参数列表(重点)
在我们 Java标准库 中提供一个很多类来创建我们的 线程池
来进行 任务的执行。
其中我们重点讲解面试中常考的这个类,ThreadPoolExecutor (很多参数) ,
因为这个 参数比较多 , 也很麻烦,下面让小编仔细到来。
1. ThreadPoolExecutor
ThreadPoolExecutor
这个类来自于 java.util.concurrent
这个包。
在这个方法中, 我们有提供七个参数, 是 面试场合中HR比较喜欢问的话题 。
下面就让小编来 细细道来 吧。
2. corePoolSize 和 maximumPoolSize
corePoolSize: 线程核心数, 就是说在这个线程中都必须有的元老级别的线程, 在创建这个线程池是就至少有这些线程。
在我们的线程池中,有两类线程: 核心线程和非核心线程
当我们 任务繁忙 时, 核心线程和非核心线程都会 被创建出来一起工作
。
当我们 任务不繁忙 时, 只需要核心线程来工作, 而非核心线程就会被销毁
。
所以一定会 执行的是核心线程,任务繁忙被销毁的 , 就是我们的非核心线程。
maximumPoolSize : 最大线程数
而我们的 最大线程数= 核心线程数 + 非核心线程数。
而真正限制 最大线程数 的主要因素就是我们的
CPU的核心数
。
鱼式疯言
核心线程: 好比我们一个公司的正式员工,无论公司是否繁忙, 公司都是需要的, 不会随意开除正式员工。
非核心线程: 好比我们一个公司的实习生, 只有当公司繁忙时,实习生才会
被录用
, 而 当公司不繁忙时 , 实习生就会被开除
。
3. keepAliveTime 和 unit
一个线程是 有可能没有任务去工作 ,这时操作系统就需要考虑要不要 回收释放这个线程资源, 就需要考虑这个线程需要 空闲的最大时间,
keepAliveTime : 允许空闲的最大时间, 一旦 实际的空闲时间 超过这个 允许空闲的最大时间 ,这个线程就会 被释放
。
unit: 允许空闲的最大时间的单位设置, 这里的单位设置就有很多种: 毫秒,妙, 日,月,年等…
鱼式疯言
允许空闲的最大时间 : 就好比一个实习生需要去不断的去工作,如果这个实习生没有任务可做,空闲下来了 ,一旦超过这个允许空闲的最大时间,公司就会把这个实习生给开除掉。
4. workQueue
在我们线程中,不同的线程可能执行不同的任务, 而这些任务该怎么存放和管理呢?
于是我们就借助了一个特殊的队列: 阻塞队列
workQueue:工作队列:对于这个队列本质上是一个Running类型的阻塞队列用于我们工作队列:
BlockingQueue<running> workQueue = new BlockingQueue<running>;
, 这个队列我们就需要把需要完成的任务都存放在这个工作队列
中, 进行工作。
鱼时疯言
此处的队列:
-
队列的大小: 程序员自己定义Size
-
队列的类型: 程序员自己决定
5. threadFactory
对于我们线程池的初始化, 我们可能会想到构造方法, 但是 构造方法也可能会出现问题 , 于是我们就引入一种设计模式(套路) :
工厂设计模式
来进行补充 我们构造方法对于线程池初始化的不足 。
构造方法的问题:
因为构造方法会对于 类型不同,数量不同 的方法会发生
重载
。
而对于 类型相同,数量相同的构造方法 来说,就不会发生
重载
,就无法实现我们多个参数的初始化
。
线程工厂: 在我们线程工厂中设计了一种普通的 成员方法(静态方法) 来通过执行我们的方法对线程池中的 成员变量
进行 初始化操作 , 从而解决构造方法重载的不足问题。
鱼式疯言
有图有真相
6. handler (重点掌握)
对于线程过多, 执行的任务超出了我们的
最大限度
, 线程池 会通过 阻塞等待的操作 来 防止任务过满 吗?
答案是 不会的
.
hander: 拒绝策略:对于任务过满,任务加载过多的情况, 我们的会根据不同的情况采用
4 种不同的策略
来安排我们下一步该怎么操作。
4种不同的策略:
<1>. AbortPolicy
这种处理方式就比较简单粗暴,当
任务过满
时, 添加任务时就会 直接抛出异常, 相当于这个无论是当前讲要执行的任务,还是之前的任务都无法执行,程序直接中断。
<2>. CallerRunspolicy
这种处理方式就比较友好,当任务过满时, 线程池会 拒绝执行新增加的任务 ,而是按照常规任务
继续执行
。
<3>. DifcardOldestPolicy
当任务过满时,这个方式就是把最老的那个任务给踢掉,让 新的任务添加进来执行。 这样保证任务
不会出现过满
的情况,也能执行到 新的任务 。
<4>. DiscardPolicy
当任务过满时, 这个方法就是在
任务队列
中, 把 最新的那个任务给踢掉
注意: 在任务队列中踢掉的任务,是 其他线程也不能执行到的
。
鱼式疯言
给小伙伴举个生活中的栗子吧 😁 😁 😁 😁
有一天小爱同学和我同时写博客, 她突然跑过来说,让我过去帮她写一下,有些东西不是很懂, 但我现在也要写博客啊,那就会采取下面4 种策略。
-
抛出异常: 我直接告诉小爱同学,今天好累啊,我都有点不想写博客了, 于是小爱同学的博客没有帮忙写,小编自己的博客同时也没写,新的任务和之前的任务都不执行 , 直接摆烂了。
-
拒绝执行: 我告诉小爱同学,你先自己搞搞呗,我这边自己还得写博客呢 ,这样新的任务我
拒绝执行
了, 我还是 继续执行我自己的任务 , 她去执行她自己的任务。 -
踢掉老任务: 小爱同学找我帮忙,于是我就把任务中最老的任务给踢掉,直接去执行她的那个新任务了。
-
踢掉新任务: 小爱同学找我帮忙,我就和小爱同学说,你先别写,先看我平常是怎么写的,于是我就把这个新任务都踢掉,大家不去执行这个新的任务 。
总结
- 线程池的初识: 线程池对于线程开销的认识以及背后更好去调度的原理理解。
- . Java标准库中线程池的参数列表(重点): 认识到对于 ThreadPoolExecutor 这个类的认识是来源于 java.util.concurrent这个包, 并且熟悉了七个参数的用法,理解了工厂设计模式,和拒绝策略的4种不同方式。
如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正
希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖