java的线程池有几种 java线程的创建方式( 七 )


关于线程池配置不合理引发的故障 , 公司内部有较多记录 , 下面举一些例子:
Case1:2018年XX页面展示接口大量调用降级 。
事故描述:XX页面展示接口产生大量调用降级 , 数量级在几十到上百 。
事故原因:该服务展示接口内部逻辑使用线程池做并行计算 , 由于没有预估好调用的流量 , 导致最大核心数设置偏小 , 大量抛出
RejectedExecutionException , 触发接口降级条件 , 示意图如下:

java的线程池有几种 java线程的创建方式


图14 线程数核心设置过小引发RejectExecutionException
Case2:2018年XX业务服务不可用S2级故障 。
事故描述:XX业务提供的服务执行时间过长 , 作为上游服务整体超时 , 大量下游服务调用失败 。
事故原因:该服务处理请求内部逻辑使用线程池做资源隔离 , 由于队列设置过长 , 最大线程数设置失效 , 导致请求数量增加时 , 大量任务堆积在队列中 , 任务执行时间过长 , 最终导致下游服务的大量调用超时失败 。示意图如下:
java的线程池有几种 java线程的创建方式


图15 线程池队列长度设置过长、corePoolSize设置过小导致任务执行速度低
业务中要使用线程池 , 而使用不当又会导致故障 , 那么我们怎样才能更好地使用线程池呢?针对这个问题 , 我们下面延展几个方向:
1. 能否不用线程池?
回到最初的问题 , 业务使用线程池是为了获取并发性 , 对于获取并发性 , 是否可以有什么其他的方案呢替代?我们尝试进行了一些其他方案的调研:

java的线程池有几种 java线程的创建方式


综合考虑 , 这些新的方案都能在某种情况下提升并行任务的性能 , 然而本次重点解决的问题是如何更简易、更安全地获得的并发性 。另外 , Actor模型的应用实际上甚少 , 只在Scala中使用广泛 , 协程框架在Java中维护的也不成熟 。这三者现阶段都不是足够的易用 , 也并不能解决业务上现阶段的问题 。
2. 追求参数设置合理性?
有没有一种计算公式 , 能够让开发同学很简易地计算出某种场景中的线程池应该是什么参数呢?
带着这样的疑问 , 我们调研了业界的一些线程池参数配置方案:

java的线程池有几种 java线程的创建方式


调研了以上业界方案后 , 我们并没有得出通用的线程池计算方式 。并发任务的执行情况和任务类型相关 , IO密集型和CPU密集型的任务运行起来的情况差异非常大 , 但这种占比是较难合理预估的 , 这导致很难有一个简单有效的通用公式帮我们直接计算出结果 。
3. 线程池参数动态化?
尽管经过谨慎的评估 , 仍然不能够保证一次计算出来合适的参数 , 那么我们是否可以将修改线程池参数的成本降下来 , 这样至少可以发生故障的时候可以快速调整从而缩短故障恢复的时间呢?基于这个思考 , 我们是否可以将线程池的参数从代码中迁移到分布式配置中心上 , 实现线程池参数可动态配置和即时生效 , 线程池参数动态化前后的参数修改流程对比如下:
java的线程池有几种 java线程的创建方式

推荐阅读