【Java】Offer快到碗里来——聊聊线程池
写在之前
Hello,大家好,我是只会写HelloWorld
的程序员大黄。
废话不多说,今天直接进入正题,聊聊Java中线程池的原理及面试问题。整体的文章会从面试题目展开,讲解面试点的过程中穿插源码的剖析。
<span >
我个人比较推崇学习方式是,先知道某个技术是干嘛的,再了解为啥有这个技术,有什么好处。只有知道了为什么、才能更好的知道是什么。
</span>
按照国际惯例先来看看一般面试中线程池会如何考察?
面试问题概览
下面是我在网上搜刮的一些关于线程池的面试题目,都是许多同学的亲身经历的。
可以先看看这些面试题目,如果你面对这些题目,该如何回答呢?
- 线程池参数的具体含义【eBay一面】
- 创建线程的方式 线程池的主要参数,线程池在提交任务之后处理的过程【携程】
- 线程池了解吗?当程序用close()方法后,线程池里面的连接会关闭吗?【京东数科】
- 你了解哪些线程池类型【小鹏汽车】
- 线程池作用有哪些作用【招商银行】
- 说说线程池操作,参数【字节跳动】
- 说一下线程池的实现,参数有哪些,不同的使用场景分别用什么线程池【阿里巴巴】
- 线程池参数?线程池为什么用new的不好?【58面试】
可以看到线程池相关的知识点被问到的概率还是挺大的,可以将问题做个分类,大概可以分为:
第一类:线程池的使用(包括如何创建线程池、线程池各个参数含义、拒绝策略、什么场景会使用)
第二类:线程池的原理了解(比如线程池底层实现、任务提交之后线程池如何工作的、线程池的执行过程)
面试回顾
面试一般面试上来肯定做一番自我介绍了,然后直奔主题。
<span >面试官:</span>大黄同学,我看你简历中写了利用线程池解决项目中性能问题,那我们先简单聊聊线程池吧。说说线程池有哪些作用?
<span >大黄:</span>面试官您好,线程池主要yo三个好处:
1、降低资源的消耗。</font>通过重复利用已经创建的线程降低线程创建和线程销毁的资源损耗。(用过的线程可以重复利用)
2、提高响应速度。当任务达到时,不需要等待线程创建就可以执行。(任务来了就可以用)
3、提供线程的可管理性。线程池本身提供了很多方法供用户监控、设置线程池。(创建一堆线程,有人帮你管理)
<span >面试官:</span>那创建线程有哪些参数可以控制呢,他们分别有什么含义呢
<span >大黄:</span>主要参数有
1、corePoolSize
:主要是线程池的核心线程数。当工作线程数小于该值时,新来的任务会创建新线程来处理,并且线程会一直存活,不会过期。
2、maximumPoolSize
:线程池的最大线程数。当工作中的线程数已经超过了最大线程数时,会默认启用拒绝策略
3、keepAliveTime
:线程空闲之后过期的时间,只有线程数大于corePoolSize
时或者开启allowCoreThreadTimeOut
参数时,该值才起作用
4、unit
:keepAliveTime
的时间单位
5、workQueue
:阻塞队列,线程数超过corePoolSize时,请求的线程会先进入阻塞队列中,只有当阻塞队列也满了,才会去创建线程
6、threadFactory
:创建线程的工厂,所有的线程都是通过该工厂创建
7、allowCoreThreadTimeOut
:是否允许核心线程过期
默认是false
,此时,核心线程空闲也不会过期;如果是true
,核心线程会等keepAliveTime
时间,然后自动过期
8、handler
:线程满时或者停止时的拒绝策略
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
....
}
<span >面试官:</span>可以详细说说线程池有哪些拒绝策略吗?
<span >大黄:</span>线程池主要由以下四种拒绝策略
调用者线程来处理被拒绝的任务。比如我线程A提交任务给线程池,如果线程池此时处理不了,被拒绝了,则会交由线程A来处理(俗话说,哪儿来的回哪儿去)
直接拒绝,并且往外抛出异常,该策略为JDK默认的拒绝策略。 线程池已经来不及处理了,偷懒,直接抛出异常。
直接废弃被拒绝的任务。线程A提交一个任务给线程池,线程池处理不了,直接不管了(俗称,装睡,假装不知道)
废弃最早被提交未被处理的请求。比如线程A提交10任务给线程池,如果再提交新的任务给线程池,线程池会先抛出最先提交的任务(俗称,渣男,喜新厌旧)
<span >面试官:</span>平时有看过线程池的底层实现吗?能简单说说线程池底层是如何做的吗?
<span >大黄:</span>线程池接受一个新提交的任务时线程池处理思路大概如下:
- 判断线程池工作中线程是否已经超过核心线程数(corePoolSize)。如果没有超过,则创建一个工作线程来执行任务;如果正在工作的线程数超过了核心线程数,则进入下一个流程。
- 判断线程池的阻塞队列(等待队列)是否已经被占满。如果没有占满,则将任务添加到阻塞队列中;如果已经占满,则进入下一阶段。
- 判断线程池工作中线程是否已经超过最大线程数。如果没有超过,则创建一个新的工作线程来执行任务;如果超过了最大线程数,则需要根据设置的拒绝策略来处理新的任务。
整体的工作原理如下:
<span >面试官:</span>嗯,可以,了解的听清楚的嘛。那你平时工作中有地方用到线程池了吗?
<span >大黄:</span>我做的一个项目中为了快速的响应用户的请求。比如在用户获取具体的商品推荐策略中,需要查询商品数据和营销数据,然后将对应的商品信息和营销信息进行组装,返回给用户。
刚开始用串行去请求,响应速度比较慢,用户迟迟查不到数据,如果一个页面半天都刷不出,用户可能就放弃查看这个商品了。为了解决这个问题,采用线程池,并发的请求商品和营销信息,缩短总体响应时间。
并行请求
串行请求
<span >面试官:</span>那你项目中设置的线程池各个参数分别是多少呢?
<span >大黄:</span>具体的线程池大小设置如下:
@BeanThreadPoolTaskExecutor ProductExecutor() {
RejectedExecutionHandler rejectedExeHandler = (r, executor) -> {
log.error("Executor_reject_handler");
throw new Exception("System.error");
};
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(200);
executor.setMaxPoolSize(1000);
executor.setKeepAliveSeconds(60);
executor.setQueueCapacity(2000);
executor.setRejectedExecutionHandler(rejectedExeHandler);
return executor;
}
<span >面试官:</span>那一般项目线程池大小设置有什么规则吗?
<span >大黄:</span>
这种需要区分任务的类型,可以分为 CPU 密集型和 I/O 密集型,根据不同的任务类型,我们计算线程数的方法也不一样。
1、CPU 密集型任务。
这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
2、I/O 密集型任务
这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。
<span >面试官:</span>好的,那我们聊聊别的吧。
<span >预告时间:下篇会仔细分析线程池的源码,可能会有点晦涩,我尽可能写的浅显一些。</span>
总结
最后大黄分享多年面试心得。面试中,面对一个问题,大概按照总分的逻辑回答即可。先直接抛出结论,然后举例论证自己的结论。一定要第一时间抓住面试官的心里,否则容易给人抓不着重点或者不着边际的印象。
本意是想着更可能回答的十全十美,无奈知识浅薄,难免会有纰漏,如果你发现了错误的地方,可以加我的微信交流。(微信公众号没有留言。)
番外
另外,关注大黄奔跑公众号,第一时间收获独家整理的面试实战记录及面试知识点总结。
我是大黄,一个只会写HelloWorld
的程序员,咱们下期见。
以上是 【Java】Offer快到碗里来——聊聊线程池 的全部内容, 来源链接: utcz.com/a/96997.html