如何创建最优的线程池?

对于每个服务端开发工程师,在日常开发中肯定会用到多线程去提高任务执行效率,谈到多线程,每个人都能熟练的给出几种多线程的使用方式。
在几种方式中,使用线程池管理线程是比较规范的方式,可以减少在创建和销毁线程上所花的时间,充分利用系统资源带给用户更快速的相应,创建一个线程池来处理并发任务看起来简单,其实不然,线程池的参数是非常有讲究的,初学者常犯的错误就是线程要么阻塞忙死,要么闲死,讲到这里不禁想问一下屏幕前的小伙伴,你觉得如何才能创建最优的线程池呢?

1.如何创建线程池
在讲解如何创建最优线程池之前,我们先来介绍一下如何标准的创建线程池。
在Java代码中,我们一般使用ThreadPoolExecutor来创建线程池,同时注入合适的参数,两者结合将线程池的使用做到最优;虽然Java提供了如newFixedThreadPool等创建线程池的方法,但是具备一定的局限性,不同的使用场景我们应该使用不同的线程池创建参数,来获取最优的性能。

线程池有哪些配置

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)**加粗样式**

maximumPoolSize:线程池最大线程数,表示在线程池中最多能创建多少个线程。如果当线程池中的数量到达这个数字时,新来的任务会抛出异常。
keepAliveTime:表示线程没有任务执行时最多能保持多少时间会停止,然后线程池的数目维持在corePoolSize。
unit:参数keepAliveTime的时间单位
workQueue:一个阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了corePoolSize大小,才会放在这里。
threadFactory:线程工厂,主要用来创建线程,比如指定线程的名字。
handler:如果线程池已满,新的任务处理方式。

如何创建最优线程池

没有一套固定的线程池创建参数使得线程池在任何环境下都是最优的,但是有最佳实践,通过先度量,再优化的流程,得出不同场景下的最优创建参数。通常过程是这样的

1.根据经验和通用共识,按照需求的顺序创建相对合理的参数

拿线程池来说,我们需要考虑线程数设置多少才合适,这个数字主要取决于服务器的cpu资源以及任务的类型。
如果任务是读写数据,那么取决于数据库临界值的连接数以及数据库的负载和性能,比如数据库单库最大链接数每秒100,如果线程数设置大于100,再多也白搭,还会给数据库带来较大的压力,甚至拖垮数据库。
如果任务是通过网络访问第三方服务,那么它取决于网络负载的大小以及第三方服务的负载和性能(和读写数据情况类似,简单来说就是取决于网络带宽和第三方服务性能)。
通常来说,cpu密集型的任务占用cpu实践比较长,线程数可以设置的小一点,IO型任务占用cpu时间比较淡,线程数可以设置大一点,我们的目的是充分利用给到我们的cpu资源,ok,根据下面这个公式,假设我们的服务器是4核,我们将将任务发送给任务执行中心的等待时间是50ms,任务中心处理每个任务耗时5ms,我们希望cpu使用率为10%,最终我们可以计算出线程数是4.4,也就是5个线程。
在这里插入图片描述
有了线程数,我们就可以通过下面的参数来配置并创建我们的线程池。
在这里插入图片描述

2.根据度量指标进行调整

使用预估值创建好线程池后,工作没有结束,我们需要通过一些度量工作来对参数进行调优。我们一般使用以下绩点作为线程池性能优劣的度量指标。

整体度量过程氛围三步
1.创建线程池并注册各项度量指标
我们可以用dropwizard的metrics库里面的InstrumentedExecutorService来帮助我们进行上述指标的统计,简单来说就是一个类似日志埋点的函数,通过不同类型的打点,统计出线程池在运行过程中的各种运行时状态参数。

2.运行线程池并收集度量指标
启动线程池后,库函数会自动记录线程池运行性能指标,并且保存在csv文件中。
在这里插入图片描述

3.观察度量指标并相应地调整参数
将csv中记录的运行时状态数据通过python可视化函数转换成图表,便于我们分析,分析中我们将重点关注四个点
1.批量任务的执行时间
2.任务在线程池队列中堆积的个数和时长
3.线程池中工作线程的变化
4.线程池由于过载而丢弃的任务

我举一个例子,通过分析线程池中线程数埋点日志,来看如何调优,我们首先将csv文件转化成图表的形式。
在这里插入图片描述
对于一个性能好的线程池来说,线程池中线程数应该是比较平稳的,避免频繁的创建和销毁线程,增加线程上下文切换带来的负担,从图中我们可以看到线程的数量波动比较明显。我们的corePoolSize、maxPoolSize和keepAliveTime都可以适当调大,增大核心线程数是因为运行过程中,线程池出现削峰的情况,增大线程保存时间,是因为通过观察可以发现45-50ms时,线程被频繁创建和销毁,增大线程存活时间,有利于提高线程利用率。

猜你喜欢

转载自blog.csdn.net/weixin_43934939/article/details/113854936