面试官问:线程池的参数是怎么配置的?不要用理论公式套!

参考美团技术文章:

Java线程池实现原理及其在美团业务中的实践 - 美团技术团队

美团面试题:线上线程池的参数,到底如何设置?

先看一个公式法

 如何合理分配线程池大小的公式法

参考回答:要合理的分配线程池的大小要根据实际情况来定、简单来说的话就是根据CPU密集和IO密集来分配。

1. 核心概念:CPU 密集型 vs. I/O 密集型

  • CPU 密集型: 任务主要消耗 CPU 资源进行计算。例如:复杂的算法、图像处理、视频编码等。

  • I/O 密集型: 任务主要等待 I/O 操作完成。例如:读写磁盘、网络请求、数据库查询等。

2. 线程池大小分配原则

  • 目标: 最大化 CPU 利用率、同时避免过多的上下文切换。

  • 关键因素: 线程等待时间与 CPU 执行时间的比例。

3. 线程池大小计算公式和方法

  • CPU 密集型:

    • N (CPU 核心数) + 1: 这是常用的建议。 +1 的目的是为了在某个线程因为缺页中断或其他原因阻塞时、CPU 可以立即切换到另一个线程,保持 CPU 的利用率。

  • I/O 密集型:

    • 2N: 这是一个常用的起点。 由于线程大部分时间都在等待 I/O、因此可以创建更多的线程来利用 CPU 的空闲时间。

    • N * (1 + WT/ST): 这是一个更通用的公式,其中:

      • N 是 CPU 核心数。

      • WT 是线程等待时间(例如等待 I/O 的时间)。

      • ST 是线程运行时间(例如CPU 执行计算的时间)。

      • WT/ST 表示线程等待时间与运行时间的比率。 比率越高、说明线程越偏向 I/O 密集型、可以增加线程数。

  • 通用业务场景 (例如定时推送消息):

    • 同样可以使用 N * (1 + WT/ST) 公式。 需要估算或测量线程在等待网络 I/O (例如发送消息) 和执行 CPU 计算 (例如,准备消息内容) 上花费的时间。

此时我们可以参考以下公式来计算线程数:

线程数=N(CPU核数)*(1+WT(线程等待时间)/ST(线程时间运行时间))

在不同的业务场景以及不同配置的部署机器中、线程池的线程数量设置是不一样的。其设置不宜过大、也不宜过小、要根据具体情况、计算出一个大概的数值、再通过实际的性能测试、计算出一个合理的线程数量

业界的一些线程池参数配置方案

有没有一种计算公式\能够让开发同学很简易地计算出某种场景中的线程池应该是什么参数呢?

调研了业界的一些线程池参数配置方案:

调研了以上业界方案后\我们并没有得出通用的线程池计算方式。

并发任务的执行情况和任务类型相关、IO密集型和CPU密集型的任务运行起来的情况差异非常大

但这种占比是较难合理预估的\这导致很难有一个简单有效的通用公式帮我们直接计算出结果。

线程池参数动态化

尽管经过谨慎的评估、仍然不能够保证一次计算出来合适的参数、那么我们是否可以将修改线程池参数的成本降下来、这样至少可以发生故障的时候可以快速调整从而缩短故障恢复的时间呢?

基于这个思考、我们是否可以将线程池的参数从代码中迁移到分布式配置中心上、实现线程池参数可动态配置和即时生效、线程池参数动态化前后的参数修改流程对比如下:

基于以上三个方向对比、我们可以看出参数动态化方向简单有效

配高配低都不合理 要知道配高配低的影响是什么

美团的动态化线程池方案:

由于难以一次性配置出合适的参数、美团采用了动态化线程池的方案、核心思想是:

  • 简化配置: 只暴露 corePoolSizemaximumPoolSize 和 workQueue (提供两种队列选择) 这三个关键参数。
  • 参数动态修改: 将线程池配置放在分布式配置中心、允许动态修改参数并立即生效。 利用 ThreadPoolExecutor 提供的 setCorePoolSizesetMaximumPoolSize 等 setter 方法。
  • 增加线程池监控: 监控线程池的运行状态,包括活跃度、任务执行情况、Reject 异常等。

监控和告警:

  • 负载监控: 通过 活跃度 = activeCount / maximumPoolSize来衡量线程池的负载。
  • 告警: 当发生 Reject 异常或队列中有等待任务 (超过阈值) 时、触发告警。
  • 任务级精细化监控: 对不同业务任务进行 Transaction 打点、了解每种任务的执行频率和耗时。
  • 运行时状态实时查看: 查看线程池的实时状态、如工作线程数、已执行任务数、队列中等待的任务数等。

总结:

设置线程池参数是一个迭代的过程、需要根据实际运行情况进行调整。 没有一劳永逸的配置。 美团的动态化线程池方案提供了一种更灵活的方式来管理线程池、通过动态调整参数和监控线程池状态、可以更好地适应业务需求的变化。 核心在于监控、没有监控、一切优化都是空谈。

设置线程池参数的步骤可以概括为:

  1. 理解核心参数和队列类型。
  2. 确定任务类型 (CPU 密集型或 IO 密集型)。
  3. 根据任务类型和公式估算初始参数。
  4. 选择合适的阻塞队列。
  5. 部署并监控线程池的运行状态 (活跃度、队列长度、Reject 异常等)。
  6. 根据监控数据动态调整参数。
  7. 设置告警阈值,及时发现问题。

美团的实践表明、动态化线程池是一种有效的解决方案、可以降低线程池参数修改的成本、并提高系统的稳定性和性能

线程池Java中用于管理和复用线程的机制,它可以提高线程的利用率和性能。线程池的七大参数包括: 1. corePoolSize(核心线程数):线程池中始终保持的线程数量,即使它们处于空闲状态。当任务数量超过核心线程数时,线程池会创建新的线程来处理任务。 2. maximumPoolSize(最大线程数):线程池中允许存在的最大线程数量。当任务数量超过最大线程数时,新的任务会被放入等待队列中等待执行。 3. keepAliveTime(线程空闲时间):当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待新任务到来时的最长等待时间。超过这个时间,空闲线程会被销毁。 4. unit(时间单位):用于设置keepAliveTime的时间单位,可以是秒、毫秒、分钟等。 5. workQueue(任务队列):用于存放等待执行的任务的队列。常见的队列类型有有界队列(如ArrayBlockingQueue)和无界队列(如LinkedBlockingQueue)。 6. threadFactory(线程工厂):用于创建新线程的工厂类。可以自定义线程的名称、优先级等属性。 7. handler(拒绝策略):当任务无法被线程池执行时的处理策略。常见的策略有直接抛出异常、丢弃任务、丢弃队列中最旧的任务等。 线程池的作用主要有以下几点: 1. 提高性能:线程池可以复用线程,避免了频繁创建和销毁线程的开销,提高了系统的性能。 2. 控制资源:通过设置核心线程数和最大线程数,可以控制系统中并发线程的数量,避免资源被过度占用。 3. 提供任务队列:线程池可以提供一个任务队列,用于存放等待执行的任务。当线程池中的线程都在执行任务时,新的任务会被放入队列中等待执行。 4. 管理线程线程池可以统一管理线程的生命周期,包括创建、销毁、空闲时间等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值