因为线程的频繁创建会消耗大量的CPU资源,为了解决线程的频繁创建,在我们的项目中大量的使用到了线程池。
1、降低资源消耗,通过重复利用已创建的线程,降低线程创建和销毁造成的资源消耗。
2、提交响应速度,当任务到达时,任务可以不需要等待线程创建就能执行。
3、提高线程的可管理性。线程是稀缺资源,如果无限的创建,不仅会消耗系统的资源,还会降低系统的稳定性,使用线程池可以统一的分配,调优和监控。
spring 中常用的创建方式,他的核心是ThreadPoolExecutor,相对于ThreadPoolExecutor做了一些扩展,支持线程名称前缀的设置。
线程池的核心,常用的线程池核心都是使用的他,比如数据库连接池、微服务调用连接池、redis连接池等。
如上图,简易的一个流量图,在我们服务运行中经常会出现小一点的波峰,但是大一点的波峰很少会出现。我们在设置核心参数时就是基于流量的情况而设置的,具体设置如下:
核心参数(处理平稳流量)是最重要的,这个参数特别难计算,因为在我们的服务中会创建多个线程池、我们系统架构中也会出现线程池。
个人的建议是根据真实的业务场景的流量+服务数量,上线之后可以观察下CPU的使用率、核心线程的使用情况,适当的进行调整即可,平稳的CPU使用率一般是在30%(这个不是固定的,一般这个比率不要太大即可)左右即可,如果大于这个值我们可以对服务进行扩容。
美团技术团队对线程池的创建做了很好的诠释,地址的话我放在参考资料中了。
当我们的服务出现小一点的峰值,核心线程都被占用了,没有多余的线程处理新来的任务了,我们需要将多余的线程给存储下来,等核心线程空闲下来继续执行。
队列的创建不建议太大,太大的话会占用大量内存、会给垃圾收集器带来特别大的压力,造成频繁GC。
我们的服务长时间出现小一点的峰值,核心线程都被占用、队列也满了,我们可以多申请一些线程,加快任务的处理。
这个大小可以在压测的时候决定,CPU的使用率可以在80%或者更大,不建议CPU的使用率超过100%。
当我们的服务出现大的波峰,服务承受不了这么大的压力,我们得考虑拒绝这些任务,不能由于量太大导致服务宕机。
拒绝策略有四种,个人建议是自定义拒绝策略+添加报警+多余任务存储或者日志方式(记录,后续方便数据恢复)
当我们的服务趋于平稳了,不需要太多的线程来处理这些任务了,我们要设置时间多久之后销毁最大线程(最大线程多久没有任务执行会进行销毁)。
这个时间根据你们的峰值间隔时间来定。
核心线程也可能会被回收,可以通过方法来设置。
可以定制线程对象的创建。
我们更多的使用为线程池中的线程设置名称,在我们监控或者排查问题时能使用到。
线程池提供了两个特别优秀的方法
/**
* 返回活跃线程数量
*/
public int getActiveCount() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int n = 0;
for (Worker w : workers)
if (w.isLocked())
++n;
return n;
} finally {
mainLock.unlock();
}
}
/**
* 获取队列,我们可以查看队列的使用数量
*/
public BlockingQueue<Runnable> getQueue() {
return workQueue;
}
这里我们能提前监控队列的使用情况,当队列增长过快或者队列快满我们可以对核心线程数进行扩容,当活跃线程数量小于核心线程数量时我们可以进行缩容。
可以监控队列里面任务的数量,增量过快或者超过快满时进行报警触发。
待完成
待完成
以上就是今天要讲的内容,本文主要写了线程池的使用、监控、问题排查、源码分析,如果有问题请大家指出来我也可以进行学习。
美团技术线程池干货:https://mp.weixin.qq.com/s/baYuX8aCwQ9PP6k7TDl2Ww