无论你是找工作,还是学习,只要聊到JUC,那一定离不开TheadPoolExecutor。可以说,它是线程池的灵魂。本篇文章就让我们聚焦ThreadPoolExecutor,一起来看看它那7个构造函数中的要素,了解一下它底层的执行流程,同时学会如何自定义我们自己的线程工厂和拒绝策略。让我们带着下面三个问题来一窥究竟。
- ThreadPoolExecutor 构造方法的7个参数了解吗?
- ThreadPoolExecutor 的执行流程了解吗?什么时候会创建临时线程?
- 如何自定义线程工厂和拒绝策略?
备注:如果不方便看文章,可以直接去看我的视频,抖音、B站搜索【程序员一棵树】。
ThreadPoolExecutor 构造方法的7个参数
首先,我们来看一下这个非常重要,并且经常被问到的构造函数,它有7个入参,截图如下。
为了更好地理解这7个入参的意思及其背后所包含的作用,我们先来看一下这个ThreadPoolExecutor对于线程的执行流程。下图是我整理好的ThreadPoolExecutor的执行流程。
执行流程说明
我们可以看到ThreadPoolExecutor的执行流程中,先后要经过如下几个判断:
- 当前线程数<核心线程数
- 阻塞队列是否已满
- 线程数>最大线程数
这边需要注意的一点就是如果当前线程数已经大于等于核心线程数了,ThreadPoolExecutor会把新来的线程丢入到阻塞队列中,直到阻塞队列满的时候才继续创建线程直到最大线程数。也就是先阻塞队列,再创建核心线程数外的线程。
那这里就有个问题需要大家思考下了,为什么要这么设计呢,先创建线程再丢阻塞队列不行吗?可以好好思考一下,我们文章的最后会进行解释。
在回到我们的流程,在上述流程中,主要涉及了如下几个概念点:
-
- 核心线程数
- 线程工厂
- 阻塞队列
- 最大线程数
- 临时线程
- 拒绝策略
在了解了流程之后,我们就可以带着上述的知识再去看一下构造函数中的7个入参分别代表什么意思了。
参数解释:
- corePoolSize: 要保留在池中的线程数,即使它们处于空闲状态,除非设置了 {@code allowCoreThreadTimeOut}
- maximumPoolSize: 池中允许的最大线程数
- keepAliveTime: 当线程数大于核心数时,多余的空闲线程等待新任务的最长时间,超过该时间则空闲线程终止。
- unit: {@code keepAliveTime} 方法参数对应的时间单位
- workQueue:存放等待执行的任务 的队列。此队列将仅包含 {@code execute} 方法提交的 {@code Runnable} 任务。
- threadFactory:executor中创建新线程时使用的线程工厂。
- handler:当线程达到上限并且队列满时,执行该策略处理新来的任务。
自定义线程工厂和拒绝策略
在日常开发过程中,我们经常会遇到一些需要我们自定义线程工厂或者拒绝策略的场景。比如需要线程池创建的线程带有业务语义,那就可能需要我们按照业务的特点去自定义工厂创建的线程名或其他属性,此时就需要我们创建自己的线程工厂,而实现方式呢则是实现ThreadFactory接口,并重写newThread方法。
下图是我们自定义线程工厂的代码截图。图中自定义了线程工厂中的每个线程都以”演示线程-“开头,后面加上当前的原子整数值
我们在日常开发过程中,还会遇到一些场景,需要自定义拒绝策略。比如我们有一些任务交给线程池去做,但是又需要当线程池打满的时候,自定义如何处理这些溢出的任务,比如落库,放到缓存中,或者丢给MQ等等。而想要自定义去处理这些溢出的任务,就需要我们自定义拒绝策略,方法是实现RejectExecutionHandler,重写rejectedExecution方法。截图如下
至此,我们已经自定义了线程工程和拒绝策略,那接下来就是我们的执行方法。同时让我们跑一下看看。
结果输出如下。
从输出结果中我们可以看到自定义的线程工厂和拒绝策略的效果。
好了,本期的文章就到这里。更多更精彩的内容请关注。
V信公众号搜索 “程序员一棵树”。 内有相关文档原文和免费资料。
D音搜索 “程序员一棵树”。
B站搜索 “程序员一棵树”。