线程池参数该怎么配置?这可能是为数不多的好答案

前言

CPU 密集型 = CPU 核数 + 1

IO 密集型 = CPU 核数 * 2

        相信这个公式可谓是线程池八股文中老生常谈的万能公式了,但现实却很骨感,我之前有个系统就是按照这个公式算出来的参数去配置的。结果效果并不好,甚至让下游系统直呼受不了。这个东西怎么说呢,还是得记住,面试的时候有用 (╯#-_-)╯╧═╧。真实场景中只能得到一个参考值,基于这个参考值,再去进行调整。但是调整这种东西可不怎么友好....

针对这种尴尬的情况,本文基于Java线程池实现原理及其在美团业务中的实践 - 美团技术团队 (meituan.com)这篇文章进行了个总结,希望给大家配置线程池参数有新的启发

美团文章调研的现有解决方案

54e9ad1097821c7c2ac74012172ee27b.png

  • 第一个就是我们上面说的,和实际业务场景有所偏离。

  • 第二个设置为 2*CPU 核心数,有点像是把任务都当做 IO 密集型去处理了。而且一个项目里面一般来说不止一个自定义线程池吧?比如有专门处理数据上送的线程池,有专门处理查询请求的线程池,这样去做一个简单的线程隔离。但是如果都用这样的参数配置的话,显然是不合理的。

  • 第三个不说了,理想状态。流量是不可能这么均衡的,就拿美团来说,下午3,4点的流量,能和 12 点左右午饭时的流量比吗?

基于上面的这些解决方案的痛点,美团给出了动态化配置的解决方案。

动态更新工作原理

b41e818d8e89bffe18a8e31cbc81f601.png

  1. 在运行期线程池使用方调用此方法设置corePoolSize之后,线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。

  2. 对于当前值小于当前工作线程数的情况,说明有多余的worker线程,此时会向当前idle的worker线程发起中断请求以实现回收,多余的worker在下次idel的时候也会被回收;

  3. 对于当前值大于原始值且当前队列中有待执行任务,则线程池会创建新的worker线程来执行队列任务,setCorePoolSize具体流程

因此动态更新线程参数的核心在于:

setCorePoolSize、setMaxNumPoolSize 以及重新设置队列长度三个方法。

丐版动态更新线程池配置组件

由于美团的文章发表只是理论上的概念并未发布源码。因此参考美团文章给出的思路我来尝试实现微服务的动态更新线程池参数的Stater.

新建一个动态调整线程池参数的Stater,命名为 iread-threadfactory

如何正确设置Java线程池参数?「建议收藏」

2: 由于需要调整最大线程数、核心线程数、队列长度三个参数,因此将三个参数做成可配置的,又因为需要辨别每个线程,因此还需要设置线程池的名字。因此建立如下配置类:

WoreadThreadFactoryProperties

@ConfigurationProperties(prefix = "woread.thread")
public class WoreadThreadFactoryProperties {

    List<Properties>list=new ArrayList<Properties>();

    public List<Properties> getList() {
        return list;
    }

    public void setList(List<Properties> list) {
        this.list = list;
    }
}

Properties

@Data
public class Properties {
    
    private String threadFactoryName;
    
    //最大线程数
    private int maximumPoolSize;
    
    //核心线程数
    private int corePoolSize;
    
    //队列大小
    private int capacity;
}

3:创建线程池创建处理类:WoreadThreadPoolExecture

public class WoreadThreadPoolExecture {

    private WoreadThreadFactoryProperties properties;
    
    private static Map<String,ThreadPoolExecutor> threadFactorys=new HashMap<String,ThreadPoolExecutor>();
    
    
    public WoreadThreadPoolExecture(WoreadThreadFactoryProperties properties) {
        this.properties=properties;
    }

    public ThreadPoolExecutor reBulidThreadFactory(String name) {

        // 获取配置列表
        List<Properties> list = properties.getList();

        // 创建线程工厂实例
        ThreadPoolExecutor threadFactory = null;

        // 如果配置列表不为空且有元素
        if (list != null && list.size() != 0) {

            // 在配置列表中查找指定名称的配置对象
            Optional<Properties> mapOpt = list
                    .stream()
                    .filter(l -> name.equals(l.getThreadFactoryName()))
                    .findFirst();

            // 根据当前运行时数据,设置默认的最大线程数、核心线程数和队列容量
            int maximumPoolSize = Runtime.getRuntime().availableProcessors() + 1;
            int corePoolSize = maximumPoolSize;
            int capacity = 1000;

            // 如果找到了指定名称的配置对象
            if (mapOpt.isPresent()) {
                Properties map = mapOpt.get();
                // 设置最大线程数
                maximumPoolSize = map.getMaximumPoolSize();
                // 设置核心线程数
                corePoolSize = map.getCorePoolSize();
                // 设置队列容量
                capacity = map.getCapacity();
            }

            // 如果线程工厂字典中已存在指定名称的线程工厂
            if (threadFactorys.containsKey(name)) {
                threadFactory = threadFactorys.get(name);
                // 更新核心线程数和最大线程数
                threadFactory.setCorePoolSize(corePoolSize);
                threadFactory.setMaximumPoolSize(maximumPoolSize);
                // 获取队列并设置容量
                WoreadLinkedBlockingQueue queue = (WoreadLinkedBlockingQueue) threadFactory.getQueue();
                queue.setCapacity(capacity);
                // 预启动所有核心线程
                threadFactory.prestartAllCoreThreads();
            } else {
                // 创建新的线程工厂实例并添加到字典中
                threadFactory = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 60, TimeUnit.SECONDS, new WoreadLinkedBlockingQueue<Runnable>(capacity));
                threadFactorys.put(name, threadFactory);
            }   
        }

        // 返回线程工厂实例
        return threadFactory;
    }
}

该类只有一个方法:ThreadPoolExecutor reBulidThreadFactory(String name)

根据线程池名字创建相应参数的线程池(如果从未创建)

根据线程池名字创建相应参数的线程池(如果已经创建—实现动态调整参数的需求)

private static Map<String,ThreadPoolExecutor> threadFactorys=new HashMap<String,ThreadPoolExecutor>(); 

利用一个静态的map存储所有创建的线程池对象

threadFactory.setCorePoolSize(corePoolSize);       
          threadFactory.setMaximumPoolSize(maximumPoolSize);       
          WoreadLinkedBlockingQueue queue=(WoreadLinkedBlockingQueue)threadFactory.getQueue();       
          queue.setCapacity(capacity);       
          threadFactory.prestartAllCoreThreads();

1-2行代码利用配置文件配置的线程数量来重新设置线程参数,可是却未找到重新设置队列长度的方法,通过翻看源码发现,

队列长度capacity被设置成了final对象,不可更改,因此我的做法是重写队列,将大小设置为可改变的,提供改变方法

创建 线程队列类:WoreadLinkedBlockingQueue

image-20231103152051376.png

4:创建staer类WoreadThreadFactoryConfiguration

@Configuration
@EnableConfigurationProperties(WoreadThreadFactoryProperties.class)
public class WoreadThreadFactoryConfiguration {
    
    @Autowired
    private WoreadThreadFactoryProperties properties;
    
    @Bean
    @ConditionalOnMissingBean(WoreadThreadPoolExecture.class)
    public WoreadThreadPoolExecture woreadThreadPoolExecture() {
        return new WoreadThreadPoolExecture(properties);
    }
}

至此动态调整参数的线程池stater构建完毕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/145341.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阶段七-Day01-SpringMVC

一、Sping MVC的介绍 1. 使用Front(前端)设计模式改写代码 1.1 目前我们的写法 目前我们所写的项目&#xff0c;持久层、业务层的类都放入到Spring容器之中了。他们之间需要注入非常方便&#xff0c;只需要通过Autowired注解即可。 但是由于Servlet整个生命周期都是被Tomca…

并查集详解(附例题和模板)

一、并查集 &#xff08;1&#xff09;处理问题的类型 1.将两个集合合并 2.询问两个元素是否在一个集合当中 询问 1.fa[x]a; 2.if(fa[x]fa[y]) o(1) 在o(1)的复杂度内进行两个操作 &#xff08;2&#xff09;基本原理 基本原理&#xff1a;每个集合用一棵树来表示&#…

振南技术干货集:深入浅出的Bootloader(5)

注解目录 1、烧录方式的更新迭代 1.1 古老的烧录方式 (怀旧一下&#xff0c;单片机高压烧录器。) 1.2 ISP 与ICP 烧录方式 (还记得当年我们玩过的 AT89S51?) 1.3 更方便的 ISP 烧录方式 1.3.1串口 ISP &#xff08;是 STC 单片机成就了我们&#xff0c;还是我们成就了…

破解tomcat密码并上传webshell

tomcat基础认证爆破 暴力破解 进入vulnhub的tomcat8目录&#xff0c;启动环境 由于tomcat密码默认最大尝试错误次数为5次&#xff0c;需要修改server.xml&#xff0c;修改下面字段 failureCount"10000000000" lockOutTime"0"tomcat默认界面&#xff0c;…

一个破单机,也要用远程缓存?

大家好&#xff0c;豆小匠终于开始Coding了&#xff0c;这期来聊聊实战相关的杂谈。 正文开始&#xff01; 作为编程萌新的时候&#xff0c;总想着把程序做复杂&#xff0c;堆技术栈。 但是程序是为场景服务的&#xff0c;比如&#xff0c;我想提高接口的响应速度&#xff0c…

传输层协议-UDP协议

目录 传输层再谈端口号端口号范围划分认识知名端口号 UDP协议UDP协议格式UDP数据封装UDP数据分用 UDP协议的特点面向数据报 UDP缓冲区UDP使用注意事项基于UDP的应用层协议 传输层 实际上我们应用层的数据并不是直接发给网络的&#xff0c;而是需要先将数据发送给传输层&#xf…

客户下单时如何自动匹配到最近的门店

有些商家有多个门店&#xff0c;当客户下单时&#xff0c;希望能够将客户下的订单分配给最近的门店。下面就具体介绍一下在采云小程中是如何实现的。 首先&#xff0c;为了简便起见&#xff0c;请确定门店高级设置保持着默认设定。因为单独的商品管理模式以及独享的商品信息模…

一篇博客读懂队列——Queue

目录 一、队列的概念和结构 ​二、队列的实现 2.1队列的初始化QueueInit 2.2队列的摧毁QueueDestroy 2.3插入结点QueuePush 2.4删除结点QueuePop 2.5返回队头QueueFront 2.6返回队尾QueueBack 2.7判断队列为空QueueEmpty 2.8统计队列数目QueueSize 一、队列的概念和…

Vue computed 计算属性

1.计算属性的相关知识 概念 &#xff1a;基于现有的数据&#xff0c;计算出来的新属性。依赖数据的变化&#xff0c;自动重新计算。 语法&#xff1a; ① 声明在 computed 配置项 中&#xff0c;一个计算属性对应一个函数 ② 使用起来和普通属性一样使用 {{ 计算属性名 …

Vue3+Element Plus表格多字段组合排序方法

一、问题描述 默认el-table是单个字段排序的&#xff0c;点击表格头排序&#xff0c;老排序字段的排序箭头样式并没有保留&#xff0c;仅仅保留了新点击字段的样式。 二、实现效果 选择多列组合排序时可以高亮多列箭头。 三、解决方法 3.1 如何记录多个字段被选择&#xff…

C++编译器对临时对象的优化

思考&#xff1a;我们在构造运算符重载号重载的时候会构造那些函数呐&#xff1f;&#xff1f;&#xff1f; 例子&#xff1a;小dome //该运算重载函数 由 左操作数调用&#xff0c;右操作数当做实参传递给该函数//触发t1t3->t1.operator (t3)Test operator (const Test &a…

js写轮播图,逐步完善

目录 1、自动轮播 2、点击更换 3、自动播放加左右箭头点击切换 4、完整版轮播图 1、自动轮播 用定时器setInterval()来写&#xff0c;可以实现自动播放 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><met…

【开源】基于JAVA的超市商品管理系统

目录 一、摘要1.1 简介1.2 项目详细录屏 二、研究内容2.1 数据中心模块2.2 超市区域模块2.3 超市货架模块2.4 商品类型模块2.5 商品档案模块 三、系统设计3.1 用例图3.2 时序图3.3 类图3.4 E-R图 四、系统实现4.1 登录4.2 注册4.3 主页4.4 超市区域管理4.5 超市货架管理4.6 商品…

http接口测试—自动化测试框架设计

一、测试需求描述 对服务后台一系列的http接口功能测试。 输入&#xff1a;根据接口描述构造不同的参数输入值&#xff08;Json格式&#xff09; 输出&#xff1a;字符串&#xff08;传入的方式传入的字符串&#xff09; http://localhost:8090/lctest/TestServer 二、程序设计…

CTFhub-RCE-命令注入

构造payload :127.0.0.1|ls 127.0.0.1|cat 80203153621323.php F12

成绩发布快捷方式

当一名老师&#xff0c;每到学期中期末&#xff0c;是不是觉得成绩发布就像个老大难&#xff1f;学生急着要知道自己的成绩&#xff0c;家长也频繁私信询问成绩&#xff0c;而传统的成绩发布方式却往往效率低下&#xff0c;费时费力。今天就来聊聊如何通过查询系统、各类代码、…

Python数据容器(集合)

集合 1.集合的定义2.集合中常用操作4.常用功能总结5.集合的特点6.练习 思考&#xff1f; 我们目前接触到了列表、元组、字符串三个数据容器了。基本满足大多数的使用场景。为何要学新的集合类型呢&#xff1f; 通过特性分析 列表可以修改、支持重复元素且有序元组、字符串不可修…

EtherNET转Profibus网关使用 AB PLC的配置方法

兴达易控EtherNET转Profibus网关&#xff08;XD-EPPB20&#xff09;是一款功能强大的通讯设备&#xff0c;具备Profibus从站功能。它的主要作用是将EtherNET/IP设备无缝接入到PROFIBUS网络中。通过连接到Profibus总线&#xff0c;它可以作为从站使用&#xff0c;并且通过连接到…

【C++】一维字符数组 与 二维字符数组

一维字符数组 一维字符数组 可以通过数组名直接进行整体输入和输出&#xff08;注意&#xff1a;当使用一维字符数组存储字符串时&#xff0c;因为元素尾部会有一个空字符\0,所以需要给空字符\0留一个位置&#xff09; char a[5]; cin>>a; cout<<a;二维字符数组 …

书单 | 11月程序员新书播报

11月最新上架计算机书籍 1、人工智能&#xff08;第3版&#xff09; 美国经典人工智能教材第3版&#xff0c;人工智能的百科全书&#xff0c;新增深度学习及人工智能编程等内容&#xff0c;理论阐释结合动手实践&#xff0c;附赠PPT课件、配套视频及代码文件。 1.人工智能经典…