javascript
Springboot线程池的使用和扩展
????我們常用ThreadPoolExecutor提供的線程池服務(wù),springboot框架提供了@Async注解,幫助我們更方便的將業(yè)務(wù)邏輯提交到線程池中異步執(zhí)行,今天我們就來(lái)實(shí)戰(zhàn)體驗(yàn)這個(gè)線程池服務(wù);
實(shí)戰(zhàn)環(huán)境
- jdk 1.8
- springboot?2.1.4.RELEASE
- 開(kāi)發(fā)工具:IntelliJ IDEA
實(shí)戰(zhàn)源碼
https://github.com/vincentduan/mavenProject.git,其中的threadpooldemoserver目錄
實(shí)戰(zhàn)步驟梳理
本次實(shí)戰(zhàn)的步驟如下:
?創(chuàng)建springboot工程
用IntelliJ IDEA創(chuàng)建一個(gè)springboot的web工程threadpooldemoserver,pom.xml內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.vincent</groupId><artifactId>threadpooldemoserver</artifactId><version>1.0-SNAPSHOT</version><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.1.4.RELEASE</version><scope>import</scope><type>pom</type></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.6</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.1.4.RELEASE</version><configuration><mainClass>cn.ac.iie.App</mainClass></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><configuration><skip>true</skip></configuration></plugin></plugins></build></project>創(chuàng)建Service層的接口和實(shí)現(xiàn)
創(chuàng)建一個(gè)service層的接口AsyncService,如下:
public interface AsyncService {/*** 執(zhí)行異步任務(wù)*/void executeAsync(); }對(duì)應(yīng)的AsyncServiceImpl,實(shí)現(xiàn)如下:
@Service @Slf4j public class AsyncServiceImpl implements AsyncService {@Overridepublic void executeAsync() {log.info("start executeAsync");try{Thread.sleep(1000);}catch(Exception e){e.printStackTrace();}log.info("end executeAsync");} }這個(gè)方法做的事情很簡(jiǎn)單:sleep了一秒鐘;
創(chuàng)建controller
創(chuàng)建一個(gè)controller為Hello,里面定義一個(gè)http接口,做的事情是調(diào)用Service層的服務(wù),如下:
@RestController @Slf4j public class Hello {@Autowiredprivate AsyncService asyncService;@RequestMapping("/")public String submit() {log.info("start submit");//調(diào)用service層的任務(wù)asyncService.executeAsync();log.info("end submit");return "success";} }至此,我們已經(jīng)做好了一個(gè)http請(qǐng)求的服務(wù),里面做的事情其實(shí)是同步的,接下來(lái)我們就開(kāi)始配置springboot的線程池服務(wù),將service層做的事情都提交到線程池中去處理;
springboot的線程池配置
創(chuàng)建一個(gè)配置類ExecutorConfig,用來(lái)定義如何創(chuàng)建一個(gè)ThreadPoolTaskExecutor,要使用@Configuration和@EnableAsync這兩個(gè)注解,表示這是個(gè)配置類,并且是線程池的配置類,如下所示:
@Configuration @EnableAsync @Slf4j public class ExecutorConfig {@Beanpublic Executor asyncServiceExecutor() {log.info("start asyncServiceExecutor");ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//配置核心線程數(shù)executor.setCorePoolSize(5);//配置最大線程數(shù)executor.setMaxPoolSize(5);//配置隊(duì)列大小executor.setQueueCapacity(99999);//配置線程池中的線程的名稱前綴executor.setThreadNamePrefix("async-service-");// rejection-policy:當(dāng)pool已經(jīng)達(dá)到max size的時(shí)候,如何處理新任務(wù)// CALLER_RUNS:不在新線程中執(zhí)行任務(wù),而是有調(diào)用者所在的線程來(lái)執(zhí)行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//執(zhí)行初始化executor.initialize();return executor;} }注意,上面的方法名稱為asyncServiceExecutor,稍后馬上用到;
將Service層的服務(wù)異步化
打開(kāi)AsyncServiceImpl.java,在executeAsync方法上增加注解@Async(“asyncServiceExecutor”),asyncServiceExecutor是前面ExecutorConfig.java中的方法名,表明executeAsync方法進(jìn)入的線程池是asyncServiceExecutor方法創(chuàng)建的,如下:
@Service @Slf4j public class AsyncServiceImpl implements AsyncService {@Override@Async("asyncServiceExecutor")public void executeAsync() {log.info("start executeAsync");try{Thread.sleep(1000);}catch(Exception e){e.printStackTrace();}log.info("end executeAsync");} }驗(yàn)證效果
?如上日志所示,我們可以看到controller的執(zhí)行線程是"nio-8080-exec-5",這是tomcat的執(zhí)行線程,而service層的日志顯示線程名為“async-service-1”,顯然已經(jīng)在我們配置的線程池中執(zhí)行了,并且每次請(qǐng)求中,controller的起始和結(jié)束日志都是連續(xù)打印的,表明每次請(qǐng)求都快速響應(yīng)了,而耗時(shí)的操作都留給線程池中的線程去異步執(zhí)行;
擴(kuò)展ThreadPoolTaskExecutor
雖然我們已經(jīng)用上了線程池,但是還不清楚線程池當(dāng)時(shí)的情況,有多少線程在執(zhí)行,多少在隊(duì)列中等待呢?這里我創(chuàng)建了一個(gè)ThreadPoolTaskExecutor的子類,在每次提交線程的時(shí)候都會(huì)將當(dāng)前線程池的運(yùn)行狀況打印出來(lái),代碼如下:
@Slf4j public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {private void showThreadPoolInfo(String prefix){ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();if(null==threadPoolExecutor){return;}log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",this.getThreadNamePrefix(),prefix,threadPoolExecutor.getTaskCount(),threadPoolExecutor.getCompletedTaskCount(),threadPoolExecutor.getActiveCount(),threadPoolExecutor.getQueue().size());}@Overridepublic void execute(Runnable task) {showThreadPoolInfo("1. do execute");super.execute(task);}@Overridepublic void execute(Runnable task, long startTimeout) {showThreadPoolInfo("2. do execute");super.execute(task, startTimeout);}@Overridepublic Future<?> submit(Runnable task) {showThreadPoolInfo("1. do submit");return super.submit(task);}@Overridepublic <T> Future<T> submit(Callable<T> task) {showThreadPoolInfo("2. do submit");return super.submit(task);}@Overridepublic ListenableFuture<?> submitListenable(Runnable task) {showThreadPoolInfo("1. do submitListenable");return super.submitListenable(task);}@Overridepublic <T> ListenableFuture<T> submitListenable(Callable<T> task) {showThreadPoolInfo("2. do submitListenable");return super.submitListenable(task);} }如上所示,showThreadPoolInfo方法中將任務(wù)總數(shù)、已完成數(shù)、活躍線程數(shù),隊(duì)列大小都打印出來(lái)了,然后Override了父類的execute、submit等方法,在里面調(diào)用showThreadPoolInfo方法,這樣每次有任務(wù)被提交到線程池的時(shí)候,都會(huì)將當(dāng)前線程池的基本情況打印到日志中;
修改ExecutorConfig.java的asyncServiceExecutor方法,將ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改為ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor(),如下所示:
@Beanpublic Executor asyncServiceExecutor() {log.info("start asyncServiceExecutor");ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();//配置核心線程數(shù)executor.setCorePoolSize(5);//配置最大線程數(shù)executor.setMaxPoolSize(5);//配置隊(duì)列大小executor.setQueueCapacity(99999);//配置線程池中的線程的名稱前綴executor.setThreadNamePrefix("async-service-");// rejection-policy:當(dāng)pool已經(jīng)達(dá)到max size的時(shí)候,如何處理新任務(wù)// CALLER_RUNS:不在新線程中執(zhí)行任務(wù),而是有調(diào)用者所在的線程來(lái)執(zhí)行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//執(zhí)行初始化executor.initialize();return executor;}修改hello.java,方便查看線程池結(jié)果:
@RequestMapping("/")public Object submit() {log.info("start submit");//調(diào)用service層的任務(wù)asyncService.executeAsync();log.info("end submit");JSONObject jsonObject = new JSONObject();ThreadPoolExecutor threadPoolExecutor = visiableThreadPoolTaskExecutor.getThreadPoolExecutor();jsonObject.put("ThreadNamePrefix", visiableThreadPoolTaskExecutor.getThreadNamePrefix());jsonObject.put("TaskCount", threadPoolExecutor.getTaskCount());jsonObject.put("completedTaskCount", threadPoolExecutor.getCompletedTaskCount());jsonObject.put("activeCount", threadPoolExecutor.getActiveCount());jsonObject.put("queueSize", threadPoolExecutor.getQueue().size());return jsonObject;}再次啟動(dòng)該工程,再瀏覽器反復(fù)刷新http://localhost:8080,看到的日志如下:
{"activeCount": 2,"queueSize": 1,"TaskCount": 26,"completedTaskCount": 23,"ThreadNamePrefix": "async-service-" }這時(shí)其中一條的返回結(jié)果。
這說(shuō)明提交任務(wù)到線程池的時(shí)候,調(diào)用的是submit(Callable task)這個(gè)方法,當(dāng)前已經(jīng)提交了26個(gè)任務(wù),完成了23個(gè),當(dāng)前有2個(gè)線程在處理任務(wù),還剩1個(gè)任務(wù)在隊(duì)列中等待,線程池的基本情況一目了然;
總結(jié)
以上是生活随笔為你收集整理的Springboot线程池的使用和扩展的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何在10亿个整数中找出前1000个最大
- 下一篇: 【5分钟系列】搭建基于docker环境搭