Offer快到碗里来—聊聊线程池
Posted 大黄奔跑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Offer快到碗里来—聊聊线程池相关的知识,希望对你有一定的参考价值。
写在之前
Hello,大家好,我是只会写HelloWorld
的程序员大黄。
废话不多说,今天直接进入正题,聊聊Java中线程池的原理及面试问题。整体的文章会从面试题目展开,讲解面试点的过程中穿插源码的剖析。
我个人比较推崇学习方式是,先知道某个技术是干嘛的,再了解为啥有这个技术,有什么好处。只有知道了为什么、才能更好的知道是什么。
按照国际惯例先来看看一般面试中线程池会如何考察?
面试问题概览
下面是我在网上搜刮的一些关于线程池的面试题目,都是许多同学的亲身经历的。
可以先看看这些面试题目,如果你面对这些题目,该如何回答呢?
线程池参数的具体含义【eBay一面】
创建线程的方式 线程池的主要参数,线程池在提交任务之后处理的过程【携程】
线程池了解吗?当程序用close()方法后,线程池里面的连接会关闭吗?【京东数科】
你了解哪些线程池类型【小鹏汽车】
线程池作用有哪些作用【招商银行】
说说线程池操作,参数【字节跳动】
说一下线程池的实现,参数有哪些,不同的使用场景分别用什么线程池【阿里巴巴】
线程池参数?线程池为什么用new的不好?【58面试】
可以看到线程池相关的知识点被问到的概率还是挺大的,可以将问题做个分类,大概可以分为:
第一类:线程池的使用(包括如何创建线程池、线程池各个参数含义、拒绝策略、什么场景会使用)
第二类:线程池的原理了解(比如线程池底层实现、任务提交之后线程池如何工作的、线程池的执行过程)
面试回顾
面试一般面试上来肯定做一番自我介绍了,然后直奔主题。
面试官:大黄同学,我看你简历中写了利用线程池解决项目中性能问题,那我们先简单聊聊线程池吧。说说线程池有哪些作用?
哈哈哈,问吧,线程池我可是早就准备过的,送分题啊。
大黄:面试官您好,线程池主要有三个好处:
1、降低资源的消耗。通过重复利用已经创建的线程降低线程创建和线程销毁的资源损耗。(用过的线程可以重复利用)
2、提高响应速度。当任务达到时,不需要等待线程创建就可以执行。(任务来了就可以用)
3、提供线程的可管理性。线程池本身提供了很多方法供用户监控、设置线程池。(创建一堆线程,有人帮你管理)
面试官:那创建线程有哪些参数可以控制呢,他们分别有什么含义呢
大黄:主要参数有
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) {
....
}
面试官:可以详细说说线程池有哪些拒绝策略吗?
大黄:线程池主要由以下四种拒绝策略
调用者线程来处理被拒绝的任务。比如我线程A提交任务给线程池,如果线程池此时处理不了,被拒绝了,则会交由线程A来处理(俗话说,哪儿来的回哪儿去)
直接拒绝,并且往外抛出异常,该策略为JDK默认的拒绝策略。 线程池已经来不及处理了,偷懒,直接抛出异常。
直接废弃被拒绝的任务。线程A提交一个任务给线程池,线程池处理不了,直接不管了(俗称,装睡,假装不知道)
废弃最早被提交未被处理的请求。比如线程A提交10任务给线程池,如果再提交新的任务给线程池,线程池会先抛出最先提交的任务(俗称,渣男,喜新厌旧)
咳咳,括号内的内容面试中就别回答了。。。
面试官:平时有看过线程池的底层实现吗?能简单说说线程池底层是如何做的吗?
其实这种问法跟上面"线程池在提交任务之后处理的过程?"本质问的问题是一样的
大黄:线程池接受一个新提交的任务时线程池处理思路大概如下:
判断线程池工作中线程是否已经超过核心线程数(corePoolSize)。如果没有超过,则创建一个工作线程来执行任务;如果正在工作的线程数超过了核心线程数,则进入下一个流程。
判断线程池的阻塞队列(等待队列)是否已经被占满。如果没有占满,则将任务添加到阻塞队列中;如果已经占满,则进入下一阶段。
判断线程池工作中线程是否已经超过最大线程数。如果没有超过,则创建一个新的工作线程来执行任务;如果超过了最大线程数,则需要根据设置的拒绝策略来处理新的任务。
大黄小提醒:可以用一个简单的例子描述,假设一个银行网点一般值班的有4个柜员,总共可以后备的柜员有10个,休息里面有可以做20人的凳子。刚开始业务不忙的时候,只有2个客户来办理业务,只要不超过4个客户,就可以用常用的值班柜员中找一个来服务;如果超过了4个,则可以让客户在休息区等待;如果等待任务超过了20个客户,还有新客户到来,则需要拉来未加班的同事了。
整体的工作原理如下:
大家可以记住这个例子,面试中不一定能够完整的回答上上面的所有内容,但是只要具体意思没有跑偏,面试官会领会到的。
面试官:嗯,可以,了解的听清楚的嘛。那你平时工作中有地方用到线程池了吗?
这点就是面试官比较聪明的地方,说了那么多,给我讲讲你工作的例子吧。正所谓:talk is cheap, show me your code.这里给大家说一个我工作中是使用的场景吧。
大黄:我做的一个项目中为了快速的响应用户的请求。比如在用户获取具体的商品推荐策略中,需要查询商品数据和营销数据,然后将对应的商品信息和营销信息进行组装,返回给用户。
刚开始用串行去请求,响应速度比较慢,用户迟迟查不到数据,如果一个页面半天都刷不出,用户可能就放弃查看这个商品了。为了解决这个问题,采用线程池,并发的请求商品和营销信息,缩短总体响应时间。
并行请求
串行请求
使用线程池也是有考量的,这种场景最重要的就是获取最大的响应速度去满足用户,所以应该不设置队列去缓冲并发任务,调高corePoolSize和maxPoolSize去尽可能创造多的线程快速执行任务。
面试官:那你项目中设置的线程池各个参数分别是多少呢?
大黄:具体的线程池大小设置如下:
@Bean
ThreadPoolTaskExecutor 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;
}
面试官:那一般项目线程池大小设置有什么规则吗?
大黄:
这种需要区分任务的类型,可以分为 CPU 密集型和 I/O 密集型,根据不同的任务类型,我们计算线程数的方法也不一样。
1、CPU 密集型任务。
这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
2、I/O 密集型任务
这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N。
上面的结论来自《Java并发编程艺术》,这种知识理论的设置,可以参考,但是实际中建议参考美团的技术博客《Java线程池实现原理及其在美团业务中的实践》
面试官:好的,那我们聊聊别的吧。
只要每个问题都可以回答到如此,何愁Offer。
预告时间:下篇会仔细分析线程池的源码,可能会有点晦涩,我尽可能写的浅显一些。
总结
最后大黄分享多年面试心得。面试中,面对一个问题,大概按照总分的逻辑回答即可。先直接抛出结论,然后举例论证自己的结论。一定要第一时间抓住面试官的心里,否则容易给人抓不着重点或者不着边际的印象。
番外
我是大黄,一个只会写HelloWorld
的程序员,咱们下期见。
以上是关于Offer快到碗里来—聊聊线程池的主要内容,如果未能解决你的问题,请参考以下文章
Android探索之旅(第三十一篇)你想要的工具类集合都在这里,快到碗里来