关于 JDK 线程池的 7 个参数和执行流程。
虽然我很久没有参加面试了,但是我觉得这题属于必考题吧。
所以如果你真的还不会,麻烦你写个 Demo ,换几个参数调试一下。把它给掌握了。
而且还得多注意由这些知识点引申出来的面试题。
比如从图片也可以看出来,JDK 线程池中如果核心线程数已经满了的话,那么后面再来的请求都是放到阻塞队列里面去,阻塞队列再满了,才会启用最大线程数。
但是你得知道,假如我们是 web 服务,请求是通过 Tomcat 进来的话,那么 Tomcat 线程池的执行流程可不是这样的。
Tomcat 里面的线程池的运行过程是:如果核心线程数用完了,接着用最大线程数,最后才提交任务到队列里面去的。这样是为了保证响应时间优先。
所以,Tomcat 的执行流程是这样的:
其技术细节就是自己重写了队列的 offer 方法。在这篇文章里面说的很清楚了,大家可以看看:
《每天都在用,但你知道 Tomcat 的线程池有多努力吗?》
好的,前面两个知识点铺垫完成了
这个题,从线程池设计的角度,我会这样去回答:
前面我们说了,10 个机器,1000 个请求并发,平均每个服务承担 100 个请求。服务器是 4 核的配置。
那么如果是 CPU 密集型的任务,我们应该尽量的减少上下文切换,所以核心线程数可以设置为 5,队列的长度可以设置为 100,最大线程数保持和核心线程数一致。
如果是 IO 密集型的任务,我们可以适当的多分配一点核心线程数,更好的利用 CPU,所以核心线程数可以设置为 8,队列长度还是 100,最大线程池设置为 10。
当然,上面都是理论上的值。
我们也可以从核心线程数等于 5 开始进行系统压测,通过压测结果的对比,从而确定最合适的设置。
同时,我觉得线程池的参数应该是随着系统流量的变化而变化的。
所以,对于核心服务中的线程池,我们应该是通过线程池监控,做到提前预警。同时可以通过手段对线程池响应参数,比如核心线程数、队列长度进行动态修改。
上面的回答总结起来就是四点:
- CPU密集型的情况。
- IO密集型的情况。
- 通过压测得到合理的参数配置。
- 线程池动态调整。
前两个是教科书上的回答,记下来就行,面试官想听到这两个答案。
后两个是更具有实际意义的回答,让面试官眼前一亮。
基于这道面试题有限的信息,设计出来的线程池队列长度其实只要大于 100 就可以。
甚至还可以设置的极限一点,比如核心线程数和最大线程数都是 4,队列长度为 96,刚好可以承担这 100 个请求,多一个都不行了。
所以这题我觉得从这个角度来说,并不是要让你给出一个完美的解决方案,而是考察你对于线程池参数的理解和技术的运用。
面试的时候我觉得这个题答到这里就差不多了。
接下来,我们再发散一下。
比如面试官问:如果我们的系统里面没有运用线程池,那么会是怎么样的呢?
首先假设我们开发的系统是一个运行在 Tomcat 容器里面的,对外提供 http 接口的 web 服务。
系统中没有运用线程池相关技术。那么我们可以直接抗住这 100 个并发请求吗?
答案是可以的。
Tomcat 里面有一个线程池。其 maxThreads 默认值是 200(假定 BIO 模式):
在 BIO 的模式下,Tomcat 的默认配置,最多可以接受到 300 (200+100)个请求。再多就是连接拒绝,connection refused。
所以,你要说处理这 100 个并发请求,那不是绰绰有余吗?
但是,如果是每秒 100 个并发请求,源源不断的过来,那就肯定是吃不消了。
这里就涉及到两个层面的修改:
- Tomcat 参数配置的调优。
- 系统代码的优化。
针对 Tomcat 参数配置的调优,我们可以适当调大其 maxThreads 等参数的值。
针对系统代码的优化,我们就可以引入线程池技术,或者引入消息队列。总之其目的是增加系统吞吐量。
同理,假设我们是一个 Dubbo 服务,对外提供的是 RPC 接口。
默认情况下,服务端使用的是 fixed 线程池,核心线程池数和最大线程数都是 200。队列长度默认为 0: