现在很多人在许多公司面试中会被问到线程池。为什么面试官这么热衷于问线程池相关的面试题呢?因为这是多线程的基础,ThreadPoolExecutor 的几个重要参数你要知道如何设置以及什么场景选择哪种 Executor 、线程池队列的选择以及相应的拒绝策略。
下面是几个大厂关于线程池的面试题:
线程池的使用场景
线程池各个参数的含义,你平时用的什么队列以及拒绝策略?
程序中哪些地方用到了线程池,用线程池的好处有哪些?
如何自己实现一个线程池
JDK 提供了哪些线程池的默认实现
阿里巴巴 Java 开发手册为啥不允许默认实现的线程池
线程池里的参数你是怎么得出来的,根据什么算出来的?
说说你自定义线程池里的工作流程
…
这里就不对面试题进行分析了,只讲核心原理再结合动态调整线程池参数的实践来帮助你对线程池有个清晰的认识,知道了原理再结合自己的实践,那面试线程池也是得心应手了。
一、线程池的概念
1.1 线程池是什么
线程池是一种线程使用模式。线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。
1.2 使用线程池的好处
降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
提高响应速度:任务到达时,无需等待线程创建即可立即执行。
提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池
ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
1.3、ThreadPoolExecutor 的核心参数
网上说的天花乱坠的,也不如直接看 Doug Lea 大佬源码的注释来的更加贴切些。
corePoolSize:the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
核心线程数:线程池中保留的线程数,即使它们是空闲的,除非设置 allowCoreThreadTimeOut。
maximumPoolSize:the maximum number of threads to allow in the pool
最大线程数:线程池中允许的最大线程数
keepAliveTime:when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
线程空闲时间:如果经过 keepAliveTime 时间后,超过核心线程数的线程还没有接受到新的任务,那就回收。
unit:the time unit for the {@code keepAliveTime} argument
单位:keepAliveTime 的时间单位
workQueue:the queue to use for holding tasks before they are executed. This queue will hold only the {@code Runnable} tasks submitted by the {@code execute} method.
存放待执行任务的队列:当提交的任务数超过核心线程数后,再提交的任务就存放在这里。它仅仅用来存放被 execute 方法提交的 Runnable 任务。
threadFactory:the factory to use when the executor creates a new thread
线程工厂:执行程序创建新线程时使用的工厂。比如我们项目中自定义的线程工厂,排查问题的时候,根据线程工厂的名称就知道这个线程来自哪里,很快地定位出问题,
handler :the handler to use when execution is blocked because the thread bounds and queue capacities are reached
拒绝策略:当队列里面放满了任务、最大线程数的线程都在工作时,这时继续提交的任务线程池就处理不了,应该执行怎么样的拒绝策略。
二、线程池的实现原理
本文描述线程池是 JDK 8 中提供的 ThreadPoolExecutor 类,那我们就从 ThreadPoolExecutor 类来看下它的 UML 依赖关系。
2.1 总体设计
- 蓝色实线:继承关系
- 绿色虚线:接口实现关系
- 绿色实现:接口继承关系
ThreadPoolExecutor 实现的顶层接口是 Executor,顶层接口只提供了void execute(Runnable command); 这么一