0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是动态线程池?动态线程池的简单实现思路

jf_ro2CN3Fa 来源:稀土掘金 2024-02-28 10:42 次阅读

什么是动态线程池?

在线程池日常实践中我们常常会遇到以下问题:

代码中创建了一个线程池却不知道核心参数设置多少比较合适。

参数设置好后,上线发现需要调整,改代码重启服务非常麻烦。

线程池相对于开发人员来说是个黑箱,运行情况在出现问题 前很难被感知。

因此,动态可监控线程池一种针对以上痛点开发的线程池管理工具。主要可实现功能有:提供对 Spring 应用内线程池实例的全局管控、应用运行时动态变更线程池参数以及线程池数据采集和监控阈值报警。

已经实现的优秀开源动态线程池

hippo4j、dynamic-tp.....

实现思路

核心管理类

需要能实现对线程池的

服务注册

获取已经注册好的线程池

以及对注册号线程池参数的刷新。

对于每一个线程池,我们使用一个线程池名字作为标识每个线程池的唯一ID。

伪代码实现

publicclassDtpRegistry{
/**
*储存线程池
*/
privatestaticfinalMapEXECUTOR_MAP=newConcurrentHashMap<>();

/**
*获取线程池
*@paramexecutorName线程池名字
*/
publicstaticExecutorgetExecutor(StringexecutorName){
returnEXECUTOR_MAP.get(executorName);
}

/**
*线程池注册
*@paramexecutorName线程池名字
*/
publicstaticvoidregistry(StringexecutorName,Executorexecutor){
//注册
EXECUTOR_MAP.put(executorName,executorWrapper);
}


/**
*刷新线程池参数
*@paramexecutorName线程池名字
*@paramproperties线程池参数
*/
publicstaticvoidrefresh(StringexecutorName,ThreadPoolPropertiesproperties){
Executorexecutor=EXECUTOR_MAP.get(executorName)
//刷新参数
//.......
}

}

如何创建线程池?

STEP 1. 我们可以使用yml配置文件的方式配置一个线程池,将线程池实例的创建交由Spring容器。

相关配置

publicclassDtpProperties{

privateListexecutors;

}

publicclassThreadPoolProperties{
/**
*标识每个线程池的唯一名字
*/
privateStringpoolName;
privateStringpoolType="common";

/**
*是否为守护线程
*/
privatebooleanisDaemon=false;

/**
*以下都是核心参数
*/
privateintcorePoolSize=1;
privateintmaximumPoolSize=1;
privatelongkeepAliveTime;
privateTimeUnittimeUnit=TimeUnit.SECONDS;
privateStringqueueType="arrayBlockingQueue";
privateintqueueSize=5;
privateStringthreadFactoryPrefix="-td-";
privateStringRejectedExecutionHandler;
}

yml example:

spring:
dtp:
executors:
#线程池1
-poolName:dtpExecutor1
corePoolSize:5
maximumPoolSize:10
#线程池2
-poolName:dtpExecutor2
corePoolSize:2
maximumPoolSize:15

STEP 2 根据配置信息添加线程池的BeanDefinition

关键类

@Slf4j
publicclassDtpImportBeanDefinitionRegistrarimplementsImportBeanDefinitionRegistrar,EnvironmentAware{
privateEnvironmentenvironment;

@Override
publicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){
log.info("注册");
//绑定资源
DtpPropertiesdtpProperties=newDtpProperties();
ResourceBundlerUtil.bind(environment,dtpProperties);
Listexecutors=dtpProperties.getExecutors();
if(Objects.isNull(executors)){
log.info("未检测本地到配置文件线程池");
return;
}
//注册beanDefinition
executors.forEach((executorProp)->{
BeanUtil.registerIfAbsent(registry,executorProp);
});
}


@Override
publicvoidsetEnvironment(Environmentenvironment){
this.environment=environment;
}
}


/**
*
*工具类
*
*/
publicclassBeanUtil{
publicstaticvoidregisterIfAbsent(BeanDefinitionRegistryregistry,ThreadPoolPropertiesexecutorProp){
register(registry,executorProp.getPoolName(),executorProp);
}

publicstaticvoidregister(BeanDefinitionRegistryregistry,StringbeanName,ThreadPoolPropertiesexecutorProp){
ClassexecutorType=ExecutorType.getClazz(executorProp.getPoolType());
Object[]args=assembleArgs(executorProp);
register(registry,beanName,executorType,args);
}

publicstaticvoidregister(BeanDefinitionRegistryregistry,StringbeanName,Classclazz,Object[]args){
BeanDefinitionBuilderbuilder=BeanDefinitionBuilder.genericBeanDefinition(clazz);
for(Objectarg:args){
builder.addConstructorArgValue(arg);
}
registry.registerBeanDefinition(beanName,builder.getBeanDefinition());
}

privatestaticObject[]assembleArgs(ThreadPoolPropertiesexecutorProp){
returnnewObject[]{
executorProp.getCorePoolSize(),
executorProp.getMaximumPoolSize(),
executorProp.getKeepAliveTime(),
executorProp.getTimeUnit(),
QueueType.getInstance(executorProp.getQueueType(),executorProp.getQueueSize()),
newNamedThreadFactory(
executorProp.getPoolName()+executorProp.getThreadFactoryPrefix(),
executorProp.isDaemon()
),
//先默认不做设置
RejectPolicy.ABORT.getValue()
};
}
}

下面解释一下这个类的作用,environment实例中储存着spring启动时解析的yml配置,所以我们spring提供的Binder将配置绑定到我们前面定义的DtpProperties类中,方便后续使用。接下来的比较简单,就是将线程池的BeanDefinition注册到IOC容器中,让spring去帮我们实例化这个bean。

STEP 3. 将已经实例化的线程池注册到核心类 DtpRegistry 中

我们注册了 beanDefinition 后,spring会帮我们实例化出来, 在这之后我们可以根据需要将这个bean进行进一步的处理,spring也提供了很多机制让我们对bean的生命周期管理进行更多的扩展。对应到这里我们就是将实例化出来的线程池注册到核心类 DtpRegistry 中进行管理。

这里我们使用 BeanPostProcessor 进行处理。

@Slf4j
publicclassDtpBeanPostProcessorimplementsBeanPostProcessor{
privateDefaultListableBeanFactorybeanFactory;

@Override
publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{
if(beaninstanceofDtpExecutor){
//直接纳入管理
DtpRegistry.registry(beanName,(DtpExecutor)bean);
}
returnbean;
}
}

这里的逻辑很简单, 就是判断一下这个bean是不是线程池,是就统一管理起来。

STEP 4. 启用 BeanDefinitionRegistrar 和 BeanPostProcessor

在springboot程序中,只要加一个@MapperScan注解就能启用mybatis的功能,我们可以学习其在spring中的启用方式,自定义一个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(DtpImportSelector.class)
public@interfaceEnableDynamicThreadPool{
}

其中,比较关键的是@Import注解,spring会导入注解中的类DtpImportSelector

而DtpImportSelector这个类实现了:

publicclassDtpImportSelectorimplementsDeferredImportSelector{
@Override
publicString[]selectImports(AnnotationMetadataimportingClassMetadata){
returnnewString[]{
DtpImportBeanDefinitionRegistrar.class.getName(),
DtpBeanPostProcessor.class.getName()
};
}
}

这样,只要我们再启动类或者配置类上加上@EnableDynamicThreadPool这个注解,spring就会将DtpImportBeanDefinitionRegistrar和DtpBeanPostProcessor这两个类加入spring容器管理,从而实现我们的线程池的注册。

@SpringBootApplication
@EnableDynamicThreadPool
publicclassApplication{
publicstaticvoidmain(String[]args){
SpringApplication.run(Application.class,args);
}
}

如何实现线程池配置的动态刷新

首先明确一点,对于线程池的实现类,例如:ThreadPoolExecutor等,都有提供核心参数对应的 set 方法,让我们实现参数修改。因此,在核心类DtpRegistry中的refresh方法,我们可以这样写:

publicclassDtpRegistry{
/**
*储存线程池
*/
privatestaticfinalMapEXECUTOR_MAP=newConcurrentHashMap<>();
/**
*刷新线程池参数
*@paramexecutorName线程池名字
*@paramproperties线程池参数
*/
publicstaticvoidrefresh(StringexecutorName,ThreadPoolPropertiesproperties){
ThreadPoolExecutorexecutor=EXECUTOR_MAP.get(executorName)

//设置参数
executor.setCorePoolSize(...);
executor.setMaximumPoolSize(...);
......
}

}

而这些新参数怎么来呢?我们可以引入Nacos、Apollo等配置中心,实现他们的监听器方法,在监听器方法里调用DtpRegistry的refresh方法刷新即可。





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 线程池
    +关注

    关注

    0

    文章

    57

    浏览量

    6844

原文标题:动态线程池的简单实现思路

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    C语言线程实现方案

    这是一个简单小巧的C语言线程实现,在 Github 上有 1.1K 的 star,很适合用来学习 Linux 的多线程编程。
    的头像 发表于 01-29 16:43 1535次阅读

    Java中的线程包括哪些

    java.util.concurrent 包来实现的,最主要的就是 ThreadPoolExecutor 类。 Executor: 代表线程的接口,有一个 execute() 方法,给一个 Runnable 类型对象
    的头像 发表于 10-11 15:33 809次阅读
    Java中的<b class='flag-5'>线程</b><b class='flag-5'>池</b>包括哪些

    动态线程思想学习及实践

    ://www.javadoop.com/post/java-thread-pool 引言 在后台项目开发过程中,我们常常借助线程实现线程任务,以此提升系统的吞吐率和响应性;而
    的头像 发表于 06-13 15:43 1182次阅读
    <b class='flag-5'>动态</b><b class='flag-5'>线程</b><b class='flag-5'>池</b>思想学习及实践

    买药秒送 JADE动态线程实践及原理浅析

    一、背景及JADE介绍 买药秒送是健康即时零售业务新的核心流量场域,面对京东首页高流量曝光,我们对频道页整个威廉希尔官方网站 架构方案进行升级,保障接口高性能、系统高可用。 动态线程是买药频道应用的威廉希尔官方网站 之一
    的头像 发表于 09-04 11:11 831次阅读
    买药秒送 JADE<b class='flag-5'>动态</b><b class='flag-5'>线程</b><b class='flag-5'>池</b>实践及原理浅析

    线程是如何实现

    线程的概念是什么?线程是如何实现的?
    发表于 02-28 06:20

    基于线程威廉希尔官方网站 集群接入点的应用研究

    本文在深入研究高级线程威廉希尔官方网站 的基础上,分析、研究了固定线程数目的线程线程数目
    发表于 01-22 14:21 5次下载

    基于Nacos的简单动态线程实现

    本文以Nacos作为服务配置中心,以修改线程核心线程数、最大线程数为例,实现一个简单
    发表于 01-06 14:14 863次阅读

    线程线程

    线程通常用于服务器应用程序。 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理
    的头像 发表于 02-28 09:53 788次阅读
    多<b class='flag-5'>线程</b>之<b class='flag-5'>线程</b><b class='flag-5'>池</b>

    Java线程核心原理

    看过Java线程源码的小伙伴都知道,在Java线程池中最核心的类就是ThreadPoolExecutor,
    的头像 发表于 04-21 10:24 851次阅读

    细数线程的10个坑

    JDK开发者提供了线程实现类,我们基于Executors组件,就可以快速创建一个线程
    的头像 发表于 06-16 10:11 724次阅读
    细数<b class='flag-5'>线程</b><b class='flag-5'>池</b>的10个坑

    线程线程怎么释放

    线程分组看,pool名开头线程占616条,而且waiting状态也是616条,这个点就非常可疑了,我断定就是这个pool开头线程导致的问题。我们先排查为何这个
    发表于 07-31 10:49 2276次阅读
    <b class='flag-5'>线程</b><b class='flag-5'>池</b>的<b class='flag-5'>线程</b>怎么释放

    Spring 的线程应用

    我们在日常开发中,经常跟多线程打交道,Spring 为我们提供了一个线程方便我们开发,它就是 ThreadPoolTaskExecutor ,接下来我们就来聊聊 Spring 的线程
    的头像 发表于 10-13 10:47 620次阅读
    Spring 的<b class='flag-5'>线程</b><b class='flag-5'>池</b>应用

    线程基本概念与原理

    、17、20等的新特性,简化了多线程编程的实现。 提高性能与资源利用率 线程主要解决两个问题:线程创建与销毁的开销以及
    的头像 发表于 11-10 10:24 529次阅读

    线程的基本概念

    线程的基本概念 不管线程是什么东西!但是我们必须知道线程被搞出来的目的就是:提高程序执行效
    的头像 发表于 11-10 16:37 521次阅读
    <b class='flag-5'>线程</b><b class='flag-5'>池</b>的基本概念

    线程的创建方式有几种

    线程是一种用于管理和调度线程的威廉希尔官方网站 ,能够有效地提高系统的性能和资源利用率。它通过预先创建一组线程并维护一个工作队列,将任务提交给线程
    的头像 发表于 12-04 16:52 857次阅读