RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

什么是動(dòng)態(tài)線程池?動(dòng)態(tài)線程池的簡單實(shí)現(xiàn)思路

jf_ro2CN3Fa ? 來源:稀土掘金 ? 2024-02-28 10:42 ? 次閱讀

什么是動(dòng)態(tài)線程池?

在線程池日常實(shí)踐中我們常常會(huì)遇到以下問題:

代碼中創(chuàng)建了一個(gè)線程池卻不知道核心參數(shù)設(shè)置多少比較合適。

參數(shù)設(shè)置好后,上線發(fā)現(xiàn)需要調(diào)整,改代碼重啟服務(wù)非常麻煩。

線程池相對于開發(fā)人員來說是個(gè)黑箱,運(yùn)行情況在出現(xiàn)問題 前很難被感知。

因此,動(dòng)態(tài)可監(jiān)控線程池一種針對以上痛點(diǎn)開發(fā)的線程池管理工具。主要可實(shí)現(xiàn)功能有:提供對 Spring 應(yīng)用內(nèi)線程池實(shí)例的全局管控、應(yīng)用運(yùn)行時(shí)動(dòng)態(tài)變更線程池參數(shù)以及線程池?cái)?shù)據(jù)采集和監(jiān)控閾值報(bào)警。

已經(jīng)實(shí)現(xiàn)的優(yōu)秀開源動(dòng)態(tài)線程池

hippo4j、dynamic-tp.....

實(shí)現(xiàn)思路

核心管理類

需要能實(shí)現(xiàn)對線程池的

服務(wù)注冊

獲取已經(jīng)注冊好的線程池

以及對注冊號線程池參數(shù)的刷新。

對于每一個(gè)線程池,我們使用一個(gè)線程池名字作為標(biāo)識每個(gè)線程池的唯一ID。

偽代碼實(shí)現(xiàn)

publicclassDtpRegistry{
/**
*儲存線程池
*/
privatestaticfinalMapEXECUTOR_MAP=newConcurrentHashMap<>();

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

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


/**
*刷新線程池參數(shù)
*@paramexecutorName線程池名字
*@paramproperties線程池參數(shù)
*/
publicstaticvoidrefresh(StringexecutorName,ThreadPoolPropertiesproperties){
Executorexecutor=EXECUTOR_MAP.get(executorName)
//刷新參數(shù)
//.......
}

}

如何創(chuàng)建線程池?

STEP 1. 我們可以使用yml配置文件的方式配置一個(gè)線程池,將線程池實(shí)例的創(chuàng)建交由Spring容器。

相關(guān)配置

publicclassDtpProperties{

privateListexecutors;

}

publicclassThreadPoolProperties{
/**
*標(biāo)識每個(gè)線程池的唯一名字
*/
privateStringpoolName;
privateStringpoolType="common";

/**
*是否為守護(hù)線程
*/
privatebooleanisDaemon=false;

/**
*以下都是核心參數(shù)
*/
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 根據(jù)配置信息添加線程池的BeanDefinition

關(guān)鍵類

@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()
),
//先默認(rèn)不做設(shè)置
RejectPolicy.ABORT.getValue()
};
}
}

下面解釋一下這個(gè)類的作用,environment實(shí)例中儲存著spring啟動(dòng)時(shí)解析的yml配置,所以我們spring提供的Binder將配置綁定到我們前面定義的DtpProperties類中,方便后續(xù)使用。接下來的比較簡單,就是將線程池的BeanDefinition注冊到IOC容器中,讓spring去幫我們實(shí)例化這個(gè)bean。

STEP 3. 將已經(jīng)實(shí)例化的線程池注冊到核心類 DtpRegistry 中

我們注冊了 beanDefinition 后,spring會(huì)幫我們實(shí)例化出來, 在這之后我們可以根據(jù)需要將這個(gè)bean進(jìn)行進(jìn)一步的處理,spring也提供了很多機(jī)制讓我們對bean的生命周期管理進(jìn)行更多的擴(kuò)展。對應(yīng)到這里我們就是將實(shí)例化出來的線程池注冊到核心類 DtpRegistry 中進(jìn)行管理。

這里我們使用 BeanPostProcessor 進(jìn)行處理。

@Slf4j
publicclassDtpBeanPostProcessorimplementsBeanPostProcessor{
privateDefaultListableBeanFactorybeanFactory;

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

這里的邏輯很簡單, 就是判斷一下這個(gè)bean是不是線程池,是就統(tǒng)一管理起來。

STEP 4. 啟用 BeanDefinitionRegistrar 和 BeanPostProcessor

在springboot程序中,只要加一個(gè)@MapperScan注解就能啟用mybatis的功能,我們可以學(xué)習(xí)其在spring中的啟用方式,自定義一個(gè)注解:

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

其中,比較關(guān)鍵的是@Import注解,spring會(huì)導(dǎo)入注解中的類DtpImportSelector

而DtpImportSelector這個(gè)類實(shí)現(xiàn)了:

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

這樣,只要我們再啟動(dòng)類或者配置類上加上@EnableDynamicThreadPool這個(gè)注解,spring就會(huì)將DtpImportBeanDefinitionRegistrar和DtpBeanPostProcessor這兩個(gè)類加入spring容器管理,從而實(shí)現(xiàn)我們的線程池的注冊。

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

如何實(shí)現(xiàn)線程池配置的動(dòng)態(tài)刷新

首先明確一點(diǎn),對于線程池的實(shí)現(xiàn)類,例如:ThreadPoolExecutor等,都有提供核心參數(shù)對應(yīng)的 set 方法,讓我們實(shí)現(xiàn)參數(shù)修改。因此,在核心類DtpRegistry中的refresh方法,我們可以這樣寫:

publicclassDtpRegistry{
/**
*儲存線程池
*/
privatestaticfinalMapEXECUTOR_MAP=newConcurrentHashMap<>();
/**
*刷新線程池參數(shù)
*@paramexecutorName線程池名字
*@paramproperties線程池參數(shù)
*/
publicstaticvoidrefresh(StringexecutorName,ThreadPoolPropertiesproperties){
ThreadPoolExecutorexecutor=EXECUTOR_MAP.get(executorName)

//設(shè)置參數(shù)
executor.setCorePoolSize(...);
executor.setMaximumPoolSize(...);
......
}

}

而這些新參數(shù)怎么來呢?我們可以引入Nacos、Apollo等配置中心,實(shí)現(xiàn)他們的監(jiān)聽器方法,在監(jiān)聽器方法里調(diào)用DtpRegistry的refresh方法刷新即可。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 線程池
    +關(guān)注

    關(guān)注

    0

    文章

    57

    瀏覽量

    6844

原文標(biāo)題:動(dòng)態(tài)線程池的簡單實(shí)現(xiàn)思路

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    C語言線程實(shí)現(xiàn)方案

    這是一個(gè)簡單小巧的C語言線程實(shí)現(xiàn),在 Github 上有 1.1K 的 star,很適合用來學(xué)習(xí) Linux 的多線程編程。
    的頭像 發(fā)表于 01-29 16:43 ?1534次閱讀

    Java中的線程包括哪些

    java.util.concurrent 包來實(shí)現(xiàn)的,最主要的就是 ThreadPoolExecutor 類。 Executor: 代表線程的接口,有一個(gè) execute() 方法,給一個(gè) Runnable 類型對象
    的頭像 發(fā)表于 10-11 15:33 ?808次閱讀
    Java中的<b class='flag-5'>線程</b><b class='flag-5'>池</b>包括哪些

    動(dòng)態(tài)線程思想學(xué)習(xí)及實(shí)踐

    ://www.javadoop.com/post/java-thread-pool? 引言 在后臺項(xiàng)目開發(fā)過程中,我們常常借助線程實(shí)現(xiàn)線程任務(wù),以此提升系統(tǒng)的吞吐率和響應(yīng)性;而
    的頭像 發(fā)表于 06-13 15:43 ?1180次閱讀
    <b class='flag-5'>動(dòng)態(tài)</b><b class='flag-5'>線程</b><b class='flag-5'>池</b>思想學(xué)習(xí)及實(shí)踐

    買藥秒送 JADE動(dòng)態(tài)線程實(shí)踐及原理淺析

    一、背景及JADE介紹 買藥秒送是健康即時(shí)零售業(yè)務(wù)新的核心流量場域,面對京東首頁高流量曝光,我們對頻道頁整個(gè)技術(shù)架構(gòu)方案進(jìn)行升級,保障接口高性能、系統(tǒng)高可用。 動(dòng)態(tài)線程是買藥頻道應(yīng)用的技術(shù)之一
    的頭像 發(fā)表于 09-04 11:11 ?830次閱讀
    買藥秒送 JADE<b class='flag-5'>動(dòng)態(tài)</b><b class='flag-5'>線程</b><b class='flag-5'>池</b>實(shí)踐及原理淺析

    線程是如何實(shí)現(xiàn)

    線程的概念是什么?線程是如何實(shí)現(xiàn)的?
    發(fā)表于 02-28 06:20

    基于線程技術(shù)集群接入點(diǎn)的應(yīng)用研究

    本文在深入研究高級線程技術(shù)的基礎(chǔ)上,分析、研究了固定線程數(shù)目的線程線程數(shù)目
    發(fā)表于 01-22 14:21 ?5次下載

    基于Nacos的簡單動(dòng)態(tài)線程實(shí)現(xiàn)

    本文以Nacos作為服務(wù)配置中心,以修改線程核心線程數(shù)、最大線程數(shù)為例,實(shí)現(xiàn)一個(gè)簡單
    發(fā)表于 01-06 14:14 ?862次閱讀

    線程線程

    線程通常用于服務(wù)器應(yīng)用程序。 每個(gè)傳入請求都將分配給線程池中的一個(gè)線程,因此可以異步處理請求,而不會(huì)占用主線程,也不會(huì)延遲后續(xù)請求的處理
    的頭像 發(fā)表于 02-28 09:53 ?786次閱讀
    多<b class='flag-5'>線程</b>之<b class='flag-5'>線程</b><b class='flag-5'>池</b>

    Java線程核心原理

    看過Java線程源碼的小伙伴都知道,在Java線程池中最核心的類就是ThreadPoolExecutor,
    的頭像 發(fā)表于 04-21 10:24 ?850次閱讀

    細(xì)數(shù)線程的10個(gè)坑

    JDK開發(fā)者提供了線程實(shí)現(xiàn)類,我們基于Executors組件,就可以快速創(chuàng)建一個(gè)線程 。
    的頭像 發(fā)表于 06-16 10:11 ?722次閱讀
    細(xì)數(shù)<b class='flag-5'>線程</b><b class='flag-5'>池</b>的10個(gè)坑

    線程線程怎么釋放

    線程分組看,pool名開頭線程占616條,而且waiting狀態(tài)也是616條,這個(gè)點(diǎn)就非??梢闪?,我斷定就是這個(gè)pool開頭線程導(dǎo)致的問題。我們先排查為何這個(gè)
    發(fā)表于 07-31 10:49 ?2274次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的<b class='flag-5'>線程</b>怎么釋放

    Spring 的線程應(yīng)用

    我們在日常開發(fā)中,經(jīng)常跟多線程打交道,Spring 為我們提供了一個(gè)線程方便我們開發(fā),它就是 ThreadPoolTaskExecutor ,接下來我們就來聊聊 Spring 的線程
    的頭像 發(fā)表于 10-13 10:47 ?620次閱讀
    Spring 的<b class='flag-5'>線程</b><b class='flag-5'>池</b>應(yīng)用

    線程基本概念與原理

    、17、20等的新特性,簡化了多線程編程的實(shí)現(xiàn)。 提高性能與資源利用率 線程主要解決兩個(gè)問題:線程創(chuàng)建與銷毀的開銷以及
    的頭像 發(fā)表于 11-10 10:24 ?528次閱讀

    線程的基本概念

    線程的基本概念 不管線程是什么東西!但是我們必須知道線程被搞出來的目的就是:提高程序執(zhí)行效
    的頭像 發(fā)表于 11-10 16:37 ?519次閱讀
    <b class='flag-5'>線程</b><b class='flag-5'>池</b>的基本概念

    線程的創(chuàng)建方式有幾種

    線程是一種用于管理和調(diào)度線程的技術(shù),能夠有效地提高系統(tǒng)的性能和資源利用率。它通過預(yù)先創(chuàng)建一組線程并維護(hù)一個(gè)工作隊(duì)列,將任務(wù)提交給線程
    的頭像 發(fā)表于 12-04 16:52 ?854次閱讀
    RM新时代网站-首页