➡️博客首页 https://blog.csdn.net/Java_Yangxiaoyuan
欢迎优秀的你点赞、️收藏、加❤️关注哦。
本文章CSDN首发,欢迎转载,要注明出处哦!
先感谢优秀的你能认真的看完本文,有问题欢迎评论区交流,都会认真回复!
线程池是池化技术的一种典型实现,所谓池化技术就是提前保存大量的资源,以备不时之需。在机器资源有限的情况下,使用池化技术可以大大的提高资源的利用率,提升性能等。
线程池,说的就是提前创建好一批线程,然后保存在线程池中,当有任务需要执行的时候,从线程池中选一个线程来执行任务。
在编程领域,比较典型的池化技术有:
线程池、连接池、内存池、对象池等
Java中线程池的继承关系如下:
线程池适合在以下情况下使用:
1. 有大量短时间任务需要处理。如果每个任务都单独开一个线程,那么线程的创建、销毁与切换会消耗相当多的资源,而线程池可以重用已有的线程,从而减少这些开销。
2. 需要管理多个任务的执行顺序。线程池可以根据任务的优先级或提交时间等因素来调度任务的执行,从而更加稳定和可控。
3. 需要限制并发执行的任务数量。通过设置线程池的最大线程数量,可以保证同时执行的任务数不会超过设定值,从而避免系统资源被过度占用。
4. 需要异步执行任务,并获取任务的执行结果。线程池可以通过返回std::future对象来实现异步执行任务,并在需要时获取任务的执行结果。
5. 并发任务处理:线程池可以用于处理并发的任务,例如处理请求、批量处理数据、并行计算等。通过线程池,可以管理和复用线程,提高任务的执行效率。
6. 异步任务执行:线程池可以用于执行异步任务,将任务提交给线程池后,可以立即返回并继续执行后续代码,不必等待任务完成。适用于需要在后台执行耗时任务,同时不阻塞主线程的场景。
线程池是一种用于管理线程的机制,它可以在程序运行时创建一定数量的线程,并将这些线程放入线程池中以供复用。线程池可以有效地减少线程的创建和销毁开销,提高系统的性能和响应速度。下面是一些线程池的优缺点:
优点:
- 降低资源消耗:线程池中的线程可以重复利用,避免了频繁地创建和销毁线程,从而降低了系统的开销。
- 提高响应速度:线程池可以预先创建一定数量的线程,当任务到达时可以直接使用这些线程,无需等待线程的创建,提高了系统的响应速度。
- 提高线程的可管理性:线程池可以进行统一的分配、调优和监控,方便对线程的管理和维护。
- 控制并发数:线程池可以限制任务的并发执行数量,避免了过多的线程导致系统资源的耗尽。
- 提供任务队列:线程池可以配合任务队列使用,缓冲尚未执行的任务,避免了任务因为线程不够而被拒绝执行的情况。
缺点:
- 无法充分利用多核资源:由于线程数量有限,当线程数量过多时,可能会浪费系统资源,并且无法充分利用多核资源。
- 无法适应动态负载变化:线程池中的线程数量是固定的,因此在面对任务负载的动态变化时,线程池可能无法做到自适应调整。
- 无法处理无限制创建线程的情况:虽然线程池可以限制任务的并发数,但是如果程序中出现无限制创建线程的情况,线程池也无法解决该问题。
- 维护成本较高:如果线程池的大小设置不当或者调优不当,可能会导致系统性能的下降或者资源的浪费,需要进行相应的调整和维护。
通常,一般构造函数会反映出这个工具或这个对象的数据存储结构。
如果把线程池比作一个公司。公司会有正式员工处理正常业务,如果工作量大的话,会雇佣外包人来工作。
闲时就可以释放外包人员以减少公司管理开销。一个公司因为成本关系,雇佣的人员始终是有最大数。
如果这时候还有任务处理不过来,就走需求池排任务。
接着,我们看一下线程池中比较重要的execute方法,该方法用于向线程池中添加一个任务。
核心模块用红框标记了:
这里逻辑稍微有点复杂,画了个流程图仅供参考:
接下来,我们看看如何添加一个工作线程的?
从方法 execute
的实现可以看出: addWorker
主要负责创建新的线程并执行任务,代码如下(这里代码有点长,没关系,也是分块的,总共有5个关键的代码块) :
判断线程池的状态,如果线程池的状态值大于或等 SHUTDOWN
,则不处理提交的任务,直接返回;
通过参数 core
判断当前需要创建的线程是否为核心线程,如果core为true,且当前线程数小于 corePoolSize
,则跳出循环,开始创建新的线程:
有人或许会疑问 retry 是什么? 这个是iava中的goto语法。只能运用在break和continue后面
接着看后面的代码:
线程池的工作线程通过Woker类实现,通过ReentrantLock锁保证线程安全。
接下来,我们看看workers是什么。
一个hashSet。所以,线程池底层的存储结构其实就是一个HashSet。
这两人钩子 (beforeExecute,afterExecute
) 允许我们自己继承线程池,做任务执行前后外理。
到这里,源代码分析到此为止。
通过上面的源码解析我们可以看到,线程池的参数主要包括以下七个:
Executors
的创建线程池的方法,创建出来的线程池都实现了ExecutorService
接口。常用方法有以下几个:
newFixedThreadPool(int Threads): 创建固定数目线程的线程池;
newCachedThreadPool(): 创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60秒钟未被使用的线程。
newSingleThreadExecutor0创建一个单线程化的Executor。
newScheduledThreadPoolint corePoolSize)创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
所谓线程池本质是一个hashSet。多余的任务会放在阻塞队列中。
只有当阻塞队列满了后,才会触发非核心线程的创建。所以非核心线程只是临时过来打杂的。直到空闲了,然后自己关闭了。
线程池提供了两个钩子(beforeExecute,afterExecute)
给我们,我们继承线程池,在执行任务前后做一些事情。
线程池原理关键技术: 锁 (lock,cas)
、阻塞队列
、hashSet (资源池)