如何设置线程池参数?美团给出了一个让面试官虎躯一震的回答。(2)

简介: 如何设置线程池参数?美团给出了一个让面试官虎躯一震的回答。(2)

先劝退一波


为了不浪费你的时间,先检测一下你是否有阅读本文的基础知识储备:


首先,我们先自定义一个线程池:


拿着这个线程池,当这个线程池在正常工作的前提下,我先问你两个问题:


1.如果这个线程池接受到了 30 个比较耗时的任务,这个时候线程池的状态(或者说数据)是怎样的?


2.在前面 30 个比较耗时的任务还没执行完成的情况下,再来多少个任务会触发拒绝策略?


其实这就是在问你线程池的执行流程了,简单的说一下就是:


1.当接收到了 30 个比较耗时的任务时,10 个核心线程数都在工作,剩下的 20 个去队列里面排队。这个时候和最大线程数是没有关系的,所以和线程存活时间也就没有关系。


2.其实你知道这个线程池最多能接受多少任务,你就知道这个题的答案是什么了,上面的线程池中最多接受 1000(队列长度) + 30(最大线程数) = 1030 个任务。所以当已经接收了30个任务的情况下,如果再来 1000 个比较耗时的任务,这个时候队列也满了,最大线程数的线程也都在工作,这个时候线程池满载了。因此,在前面 30 个比较耗时的任务还没执行完成的情况下,再来 1001 个任务,第 1001 个任务就会触发线程池的拒绝策略了。


这两个问题你得会,如果答不上来你也别往下看了,大概率看的一脸懵逼。


我建议你先给本文点个赞,接着去网上搜一下线程池执行流程的文章(其实美团的那篇文章也写了执行流程),写个 Demo 跑一下,摸清楚了,再来看这篇文章。


巨人肩膀


对于线程池参数到底如何设置的问题美团的那篇文章提供了一个很好的思路和解决方案,展现的是一个大而全的东西。


但是,对于实施起来的细节就没有具体的展示了。


所以文本斗胆,站在巨人的肩膀上对细节处进行一些补充说明。



1.现有的解决方案的痛点。


2.动态更新的工作原理是什么?


3.动态设置的注意点有哪些?


4.如何动态指定队列长度?


5.这个过程中涉及到的面试题有哪些?


下面从这五点进行展开说明。


现有的解决方案的痛点。


现在市面上大多数的答案都是先区分线程池中的任务是 IO 密集型还是 CPU 密集型。


如果是 CPU 密集型的,可以把核心线程数设置为核心数+1。

为什么要加一呢?


《Java并发编程实战》一书中给出的原因是:即使当计算(CPU)密集型的线程偶尔由于页缺失故障或者其他原因而暂停时,这个“额外”的线程也能确保 CPU 的时钟周期不会被浪费。


看不懂是不是?没关系我也看不懂。反正把它理解为一个备份的线程就行了。


这个地方还有个需要注意的小点就是,如果你的服务器上部署的不止一个应用,你就得考虑其他的应用的线程池配置情况。


经过精密的计算,你咔一下设置为核心数,结果项目部署上去了,发现还有其他的应用在和你抢 CPU,你想想难不难受。


如果是包含 IO 操作的任务呢?这个才是我们关心的东西。


《Java并发编程实战》一书中给出的计算方式是这样的:


理想很丰满,现实很骨感。


我之前有个系统就是按照这个公式算出来的参数去配置的。

结果效果并不好,甚至让下游系统直呼受不了。


这个东西怎么说呢,还是得记住,面试的时候有用。真实场景中只能得到一个参考值,基于这个参考值,再去进行调整。


我们再看一下美团的那篇文章调研的现有解决方案列表:


第一个就是我们上面说的,和实际业务场景有所偏离。


第二个设置为 2*CPU 核心数,有点像是把任务都当做 IO 密集型去处理了。而且一个项目里面一般来说不止一个自定义线程池吧?比如有专门处理数据上送的线程池,有专门处理查询请求的线程池,这样去做一个简单的线程隔离。但是如果都用这样的参数配置的话,显然是不合理的。


第三个不说了,理想状态。流量是不可能这么均衡的,就拿美团来说,下午3,4点的流量,能和 12 点左右午饭时的流量比吗?


基于上面的这些解决方案的痛点,美团给出了动态化配置的解决方案。


动态更新的工作原理是什么?


先来一个动态更新的代码示例:


上面的程序就是自定义了一个核心线程数为 2,最大线程数为 5,队列长度为 10 的线程池。


然后给它塞 15 个耗时 10 秒的任务,直接让它 5 个最大线程都在工作,队列长度 10 个都塞满。


当前的情况下,队列里面的 10 个,前 5 个在 10 秒后会被执行,后 5 个在 20 秒后会被执行。


再加上最大线程数正在执行的 5 个,15 个任务全部执行完全需要 3 个 10 秒即 30 秒的时间。


这个时候,如果我们把核心线程数和最大线程数都修改为 10。


那么 10 个任务会直接被 10 个最大线程数接管,10 秒就会被处理完成。


剩下的 5 个任务会在 10 秒后被执行完成。


所以,15 个任务执行完成需要 2 个 10 秒即 20 秒的时间处理完成了。


看一下上面程序的打印日志:


效果实现了,我先看一下原理是什么。


先看 setCorePoolSize 方法:



这个方法在美团的文章中也说明了:


在运行期线程池使用方调用此方法设置corePoolSize之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。


对于当前值小于当前工作线程数的情况,说明有多余的worker线程,此时会向当前idle的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收;


对于当前值大于原始值且当前队列中有待执行任务,则线程池会创建新的worker线程来执行队列任务,setCorePoolSize具体流程如下:


看了美团的那篇文章后,我又去看了 Spring 的 ThreadPoolTaskExecutor类 (就是对JDK ThreadPoolExecutor 的一层包装,可以理解为装饰者模式)的 setCorePoolSize 方法: 注释上写的清清楚楚,可以在线程池运行时修改该参数。


而且,你再品一品 JDK 的源码,其实源码也体现出了有修改的含义的,两个值去做差值,只是第一次设置的时候原来的值为 0 而已。


哎,当时没有细细研究,恨自己看源码的时候不仔细。


接着看 setMaximumPoolSize 源码:


这个地方就很简单了,逻辑不太复杂。


1.首先是参数合法性校验。


2.然后用传递进来的值,覆盖原来的值。


3.判断工作线程是否是大于最大线程数,如果大于,则对空闲线程发起中断请求。


经过前面两个方法的分析,我们知道了最大线程数和核心线程数可以动态调整。

目录
相关文章
|
14天前
|
消息中间件 架构师 Java
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
|
1月前
|
人工智能 算法 数据库
美团面试:LLM大模型存在哪些问题?RAG 优化有哪些方法?_
美团面试:LLM大模型存在哪些问题?RAG 优化有哪些方法?_
|
4月前
|
监控 Kubernetes Java
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
|
4月前
|
存储 关系型数据库 MySQL
美团面试:MySQL为什么 不用 Docker部署?
45岁老架构师尼恩在读者交流群中分享了关于“MySQL为什么不推荐使用Docker部署”的深入分析。通过系统化的梳理,尼恩帮助读者理解为何大型MySQL数据库通常不使用Docker部署,主要涉及性能、管理复杂度和稳定性等方面的考量。文章详细解释了有状态容器的特点、Docker的资源隔离问题以及磁盘IO性能损耗,并提供了小型MySQL使用Docker的最佳实践。此外,尼恩还介绍了Share Nothing架构的优势及其应用场景,强调了配置管理和数据持久化的挑战。最后,尼恩建议读者参考《尼恩Java面试宝典PDF》以提升技术能力,更好地应对面试中的难题。
|
3月前
|
数据采集 Java Linux
面试大神教你:如何巧妙回答线程优先级这个经典考题?
大家好,我是小米。本文通过故事讲解Java面试中常见的线程优先级问题。小明和小华的故事帮助理解线程优先级:高优先级线程更可能被调度执行,但并非越高越好。实际开发需权衡业务需求,合理设置优先级。掌握线程优先级不仅能写出高效代码,还能在面试中脱颖而出。最后,小张因深入分析成功拿下Offer。希望这篇文章能助你在面试中游刃有余!
68 4
面试大神教你:如何巧妙回答线程优先级这个经典考题?
|
3月前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
218 14
|
2月前
|
Java
线程池的核心参数有哪些 ?
corePoolSize 核心线程数量 maximumPoolSize 最大线程数量 keepAliveTime 线程保持时间,N个时间单位 unit 时间单位(比如秒,分) workQueue 阻塞队列 threadFactory 线程工厂 handler 线程池拒绝策略
|
3月前
|
存储 NoSQL 前端开发
美团面试:手机扫描PC二维码登录,底层原理和完整流程是什么?
45岁老架构师尼恩详细梳理了手机扫码登录的完整流程,帮助大家在面试中脱颖而出。该过程分为三个阶段:待扫描阶段、已扫描待确认阶段和已确认阶段。更多技术圣经系列PDF及详细内容,请关注【技术自由圈】获取。
|
3月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
157 6
|
4月前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
161 16