• 线程池的使用、问题排查、源码分析等


    前言

    因为线程的频繁创建会消耗大量的CPU资源,为了解决线程的频繁创建,在我们的项目中大量的使用到了线程池。
    1、降低资源消耗,通过重复利用已创建的线程,降低线程创建和销毁造成的资源消耗。
    2、提交响应速度,当任务到达时,任务可以不需要等待线程创建就能执行。
    3、提高线程的可管理性。线程是稀缺资源,如果无限的创建,不仅会消耗系统的资源,还会降低系统的稳定性,使用线程池可以统一的分配,调优和监控。

    一、java中常用的线程池创建方式

    ThreadPoolTaskExecutor

    spring 中常用的创建方式,他的核心是ThreadPoolExecutor,相对于ThreadPoolExecutor做了一些扩展,支持线程名称前缀的设置。

    ThreadPoolExecutor

    线程池的核心,常用的线程池核心都是使用的他,比如数据库连接池、微服务调用连接池、redis连接池等。

    二、ThreadPoolExecutor 使用

    首先我们先观察下我们的流量

    在这里插入图片描述
    如上图,简易的一个流量图,在我们服务运行中经常会出现小一点的波峰,但是大一点的波峰很少会出现。我们在设置核心参数时就是基于流量的情况而设置的,具体设置如下:

    1、核心线程数

    核心参数(处理平稳流量)是最重要的,这个参数特别难计算,因为在我们的服务中会创建多个线程池、我们系统架构中也会出现线程池。
    个人的建议是根据真实的业务场景的流量+服务数量,上线之后可以观察下CPU的使用率、核心线程的使用情况,适当的进行调整即可,平稳的CPU使用率一般是在30%(这个不是固定的,一般这个比率不要太大即可)左右即可,如果大于这个值我们可以对服务进行扩容。
    美团技术团队对线程池的创建做了很好的诠释,地址的话我放在参考资料中了。

    2、队列

    当我们的服务出现小一点的峰值,核心线程都被占用了,没有多余的线程处理新来的任务了,我们需要将多余的线程给存储下来,等核心线程空闲下来继续执行。
    队列的创建不建议太大,太大的话会占用大量内存、会给垃圾收集器带来特别大的压力,造成频繁GC。

    3、最大线程数

    我们的服务长时间出现小一点的峰值,核心线程都被占用、队列也满了,我们可以多申请一些线程,加快任务的处理。
    这个大小可以在压测的时候决定,CPU的使用率可以在80%或者更大,不建议CPU的使用率超过100%。

    4、拒绝策略

    当我们的服务出现大的波峰,服务承受不了这么大的压力,我们得考虑拒绝这些任务,不能由于量太大导致服务宕机。
    拒绝策略有四种,个人建议是自定义拒绝策略+添加报警+多余任务存储或者日志方式(记录,后续方便数据恢复)

    5、线程没有任务生存时间

    当我们的服务趋于平稳了,不需要太多的线程来处理这些任务了,我们要设置时间多久之后销毁最大线程(最大线程多久没有任务执行会进行销毁)。
    这个时间根据你们的峰值间隔时间来定。
    核心线程也可能会被回收,可以通过方法来设置。

    6、线程工厂

    可以定制线程对象的创建。
    我们更多的使用为线程池中的线程设置名称,在我们监控或者排查问题时能使用到。

    在我们使用线程池时有没有需要注意的地方

    1、我们可以实现自动扩容或者缩容

    线程池提供了两个特别优秀的方法

        /**
        * 返回活跃线程数量
        */
        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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里我们能提前监控队列的使用情况,当队列增长过快或者队列快满我们可以对核心线程数进行扩容,当活跃线程数量小于核心线程数量时我们可以进行缩容

    2、提前监控

    可以监控队列里面任务的数量,增量过快或者超过快满时进行报警触发。

    3、常见错误排查

    待完成

    三、ThreadPoolExecutor源码分析

    待完成

    总结

    以上就是今天要讲的内容,本文主要写了线程池的使用、监控、问题排查、源码分析,如果有问题请大家指出来我也可以进行学习。

    参考资料

    美团技术线程池干货:https://mp.weixin.qq.com/s/baYuX8aCwQ9PP6k7TDl2Ww

  • 相关阅读:
    (2.4)【服务型木马-winshell】最小木马:使用方法
    工欲善其事,必先利其器,这5款利器推荐你
    通过cpolar内网穿透,在家实现便捷的SSH远程连接公司内网服务器教程
    ESP32网络开发实例-HTTP-GET请求
    牛X,试用了下 GitHub 上 2 万 Star 的第一抢票神器,3 秒钟抢到!
    Phoenix的二级索引
    抓包神器TCPDUMP的分析总结-涵盖各大使用场景、高级用法
    ChatGPT支持下的PyTorch机器学习与深度学习技术应用
    站在自动装配角度浅析Spring和SpringBoot的区别
    Web界面自动化操作工具 - Selenium常见用法
  • 原文地址:https://blog.csdn.net/shibai906/article/details/125460213