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

news2024/10/7 5:26:45

前言

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构建完毕。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

清华镜像源地址,适用于pip下载速度过慢从而导致下载失败的问题

清华地址 https://pypi.tuna.tsinghua.edu.cn/simple下载各种各样的包的指令模板 pip install XXX -i https://pypi.tuna.tsinghua.edu.cn/simple这样就行了&#xff0c;XXX代表的是你将要下载的包名称。 比如&#xff1a; pip install opencv-python -i https://pypi.tuna.…

数据结构-二叉树的前、中、后序遍历

目录 1. 二叉树的遍历 1.1 前序 1.2 中序 1.3 后序 1.4 遍历的复杂度 2.二叉树节点个数及高度的计算 2.1 二叉树节点个数 2.2 二叉树叶子节点的个数 2.3 二叉树高度 2.4 二叉树第k层节点个数 1. 二叉树的遍历 前面的章节中&#xff0c;我们学习了二叉树的顺序结构&am…

计算机毕业设计选题推荐-体育赛事微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

c语言11周(16~20)

利用函数求和 //只填写要求的函数 double fun(int n) {double s 0;int i;for (i 1; i < n; i) {s 1.0 / (i * i);}return s; } 编写char fun(char c)函数&#xff0c;将数字参数字符c按如下规则转换。 题干编写char fun(char c)函数&#xff0c;将数字参数字符c按如…

【milkv】1、光感bh1750驱动添加及测试

前言 本章介绍在milkv-duo开发板上添加光感bh1750&#xff0c;并实现应用层测试。 一、电路图查看 1.1 duo开发板i2c引脚 https://github.com/milkv-duo/duo-files 这些都是可以作为i2c使用的引脚 注意&#xff1a;电路图中的gpio0、1对应的是芯片上的gpio28、29&#…

为什么Springboot项目中有些写法继承了SpringBootServletInitializer类?Springboot的两种发布方式

文章目录 一、前言二、SpringBoot的两种发布方式2.1、内置容器运行2.2、外置容器&#xff08;Tomcat&#xff09;运行 三、扩展3.1、如何将 Spring Boot 项目打包成 war 包&#xff1f; 一、前言 在一次SpringBoot源码中看到了启动类中继承了SpringBootServletInitializer&…

EMC-4641C运动控制器固件升级

EMC-4641C运动控制器固件升级 更新前查看运动控制器固件版本 点击打开ESMTPTest_X64R.exe程序 点击搜索&#xff0c;程序自动选择IP为192.168.0.135的运动控制器&#xff08;实际操作时候可能会因为现场情况&#xff0c;IP会有细微不同&#xff09; 点击Connest 等待几秒后在…

『数据结构与算法』散列表(哈希表)

1. 什么是散列表 散列表&#xff08;Hash Table&#xff09;也叫哈希表&#xff0c;是根据给定关键字&#xff08;Key&#xff09;来计算出该关键字在表中存储地址的数据结构。也就是说&#xff0c;散列表建立了关键字与存储地址之间的一种直接映射关系&#xff0c;将关键字映…

【电路笔记】-诺顿定理(Norton‘s Theorem)

诺顿定理&#xff08;Norton’s Theorem&#xff09; 文章目录 诺顿定理&#xff08;Nortons Theorem&#xff09;1、概述与定义2、诺顿模型确定3、一些线性电路的诺顿模型3.1 单电压源3.2 单电流源3.3 多电流/电压源 5、总结 本文是我们上一篇有关戴维南定理的文章的延续。 在…

[文件读取]lanproxy 文件读取 (CVE-2021-3019)

1.1漏洞描述 漏洞编号CVE-2021-3019漏洞类型文件读取漏洞等级⭐漏洞环境VULFOCUS攻击方式 描述: Lanproxy 路径遍历漏洞通过../绕过读取任意文件。该漏洞允许目录遍历读取/../conf/config.properties来获取到内部网连接的凭据。 1.2漏洞等级 高危 1.3影响版本 Lanproxy 1.4漏洞…

酷柚易汛ERP-购货订单操作指南

1、应用场景 先下购货订单&#xff0c;收货入库后生成购货单。 2、主要操作 2.1 新增购货订单 打开【购货】-【购货订单】新增购货订单。&#xff08;*为必填项&#xff0c;其他为选填&#xff09; ① 录入供应商&#xff1a;点击供应商字段框的 &#xff0c;在弹框中选择供…

漏洞-任意账号注册

一漏洞介绍 1.未验证邮箱/手机号 情景&#xff1a;应用为了方便用户记录用户名&#xff0c;使用邮箱和手机号作为用户名&#xff08;因此很多应用在注册的时候就要求用户填写&#xff0c;多数时候都会给用户发送激活信息&#xff0c;激活后才能登录&#xff09; 缺陷&#xff…

Linux Mint 21.3 将搭载 Cinnamon 6.0 和实验性 Wayland 支持

导读Wayland 会话可能在 Linux Mint 23 系列中成为默认选项&#xff0c;预计将在 2026 年实现。 Linux Mint 项目今天在他们的每月新闻通讯中 宣布&#xff0c;他们已经开始着手在未来的 Linux Mint 发行版中实施 Wayland 会话&#xff0c;最初将在 Linux Mint 21.3 中提供。 …

3DMAX建模基础教程:常用工具补充

在本篇3DMAX建模基础教程中&#xff0c;我们将为您介绍一些常用的工具及其功能。熟练掌握这些工具将大大提高您的建模效率。 1️⃣ 选择与变换工具 选择工具&#xff1a;帮助您选择对象&#xff0c;可以通过单击对象或按组选择。 变换工具&#xff1a;对选定的对象进行移动、…

【milkv】0、duo编译环境搭建

一、开发资料整理 Docker https://hub.docker.com/repository/docker/dreamcmi/cv1800-docker/general GitHub https://github.com/milkv-duo/duo-buildroot-sdk CV181x/CV180x MMF SDK 开发文档汇总 https://developer.sophgo.com/thread/471.html cv181x芯片使用的交叉…

【计算机网络】UDP协议

UDP的结构 我们学习一个协议最主要的就是理解它的报文格式&#xff0c;对于UDP协议来说 我们看下面的这张图。 16位UDP长度&#xff0c;表示整个数据报&#xff08;UDP首部UDP数据&#xff09;的最大长度。UDP报文长度占两个字节&#xff0c;16位表示的数据范围&#xff08;0-…

新版本Idea设置启动参数

1.进入配置页面 2.点击下图红框的部分&#xff0c;会看到有很多操作可选 3.选择添加VM参数即可 此时就会多出一个可以输入参数的框了&#xff0c;如下&#xff1a;

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

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

酷柚易汛ERP- 组装单与拆卸单操作

1、功能介绍 组装单用来处理企业组装等加工业务&#xff0c;拆卸单用来处理企业拆卸等加工业务&#xff0c;支持一对多的产品加工业务。 2、主要操作 2.1 新增组装单 打开【仓库】-【组装单】新增组装单。 录入组合件与子件&#xff0c;单据审核后&#xff0c;系统根据存货…

如何在Qemu上跑Milk-duo开发板

前言 &#xff08;1&#xff09;PLCT实验室实习生长期招聘&#xff1a;招聘信息链接 &#xff08;2&#xff09;学习本文之前&#xff0c;要求先看一下Milk-V Duo快速上手的环境搭建部分&#xff0c;创建好镜像文件。 正文 编译milk-duo qemu &#xff08;1&#xff09;下面步…