线程池----上
本文最后更新于 2025-03-07,文章超过7天没更新,应该是已完结了~
一、什么是线程池
线程池其实是一种池化的技术的实现,池化技术的核心思想其实就是实现资源的一个复用,避免资源的重复创建和销毁带来的性能开销。在线程池中,线程池可以管理一堆线程,让线程执行完任务之后不会进行销毁,而是继续去处理其它线程已经提交的任务。
线程池的好处:
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统 的稳定性,使用线程池可以进行统一的分配,调优和监控。
二、线程池的构造
Java中主要是通过构建ThreadPoolExecutor来创建线程池的,接下来我们看一下线程池是如何构造出来的。
corePoolSize:线程池中用来工作的核心的线程数量。
maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
keepAliveTime:超出 corePoolSize 后创建的线程( 非核心线程 )存活时间或者是所有线程最大存活时间,取决于配置。
unit:keepAliveTime 的时间单位。
workQueue:任务队列,是一个阻塞队列,当线程数已达到核心线程数,会将任务存储在阻塞队列中。
threadFactory :线程池内部创建线程所用的工厂。
handler:拒绝策略;当队列已满并且线程数量达到最大线程数量时,会调用该方法处理该任务。
线程池的构造其实很简单,就是传入一堆参数,然后进行简单的赋值操作。
三、线程池的运行原理
说完线程池的核心构造参数的意思,接下来就来画图讲解这些参数在线程池中是如何工作的。
线程池刚创建出来是什么样子呢,如下图
不错,刚创建出来的线程池中只有一个构造时传入的阻塞队列而已,此时里面并没有的任何线程,但是如果你想要在执行之前已经创建好核心线程数,可以调用prestartAllCoreThreads方法来实现,默认是没有线程的。
当有线程通过execute方法提交了一个任务,会发生什么呢?
提交任务的时候,其实会去进行任务的处理
首先会去判断当前线程池的线程数是否小于核心线程数,也就是线程池构造时传入的参数corePoolSize。
如果小于,那么就直接通过ThreadFactory创建一个线程来执行这个任务,如图
当任务执行完之后,线程不会退出,而是会去从阻塞队列中获取任务,如下图
接下来如果又提交了一个任务,也会按照上述的步骤,去判断是否小于核心线程数,如果小于,还是会创建线程来执行任务,执行完之后也会从阻塞队列中获取任务。这里有个细节,就是提交任务的时候,就算有线程池里的线程从阻塞队列中获取不到任务,如果线程池里的线程数还是小于核心线程数,那么依然会继续创建线程,而不是复用已有的线程。
如果线程池里的线程数不再小于核心线程数呢?那么此时就会尝试将任务放入阻塞队列中,入队成功之后,如图
这样在阻塞的线程就可以获取到任务了。
但是,随着任务越来越多,队列已经满了,任务放入失败了,那怎么办呢?
此时就会判断当前线程池里的线程数是否小于最大线程数,也就是入参时的maximumPoolSize参数
如果小于最大线程数,那么也会创建非核心线程来执行提交的任务,如图
所以,从这里可以发现,就算队列中有任务,新创建的线程(非核心线程)还是优先处理这个提交的任务,而不是从队列中获取已有的任务执行,从这可以看出,先提交的任务不一定先执行。
但是不幸的事发生了,线程数已经达到了最大线程数量,那么此时会怎么办呢?
此时就会执行拒绝策略,也就是构造线程池的时候,传入的RejectedExecutionHandler对象,来处理这个任务。
RejectedExecutionHandler的实现JDK自带的默认有4种
AbortPolicy:丢弃任务,抛出运行时异常
CallerRunsPolicy:由提交任务的线程来执行任务
DiscardPolicy:丢弃这个任务,但是不抛异常
DiscardOldestPolicy:从队列中剔除最先进入队列的任务,然后再次提交任务
线程池创建的时候,如果不指定拒绝策略就默认是AbortPolicy策略。当然,你也可以自己实现RejectedExecutionHandler接口,比如将任务存在数据库或者缓存中,这样就数据库或者缓存中获取到被拒绝掉的任务了。
到这里,我们发现,线程池构造的几个参数corePoolSize、maximumPoolSize、workQueue、threadFactory、handler我们都在上述的执行过程中讲到了,那么还差两个参数keepAliveTime和unit(unit是keepAliveTime的时间单位)没讲到,所以keepAliveTime是如何起到作用的呢,这个问题留到后面分析。
说完整个执行的流程,接下来看看execute方法代码是如何实现的。
workerCountOf(c)<corePoolSize:这行代码就是判断是否小于核心线程数,是的话就通过addWorker方法,addWorker就是添加线程来执行任务。
workQueue.offer(command):这行代码就表示尝试往阻塞队列中添加任务
添加失败之后就会再次调用addWorker方法尝试添加非核心线程来执行任务
如果还是添加非核心线程失败了,那么就会调用reject(command)来拒绝这个任务。
最后再来另画一张图总结execute执行流程
- 感谢你赐予我前进的力量