新聞中心
今天就跟大家聊聊有關(guān)如何使用Springboot線(xiàn)程池,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
創(chuàng)新互聯(lián)建站2013年至今,先為平房等服務(wù)建站,平房等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢(xún)服務(wù)。為平房企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。
創(chuàng)建springboot工程
用IntelliJ IDEA創(chuàng)建一個(gè)springboot的web工程threadpooldemoserver,pom.xml內(nèi)容如下:
4.0.0 com.vincent threadpooldemoserver 1.0-SNAPSHOT UTF-8 1.8 1.8 org.springframework.boot spring-boot-dependencies 2.1.4.RELEASE import pom org.springframework.boot spring-boot-starter-web org.projectlombok lombok 1.18.6 org.springframework.boot spring-boot-maven-plugin 2.1.4.RELEASE cn.ac.iie.App repackage org.apache.maven.plugins maven-surefire-plugin true
創(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 { @Override public 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 { @Autowired private 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的線(xiàn)程池服務(wù),將service層做的事情都提交到線(xiàn)程池中去處理;
springboot的線(xiàn)程池配置
創(chuàng)建一個(gè)配置類(lèi)ExecutorConfig,用來(lái)定義如何創(chuàng)建一個(gè)ThreadPoolTaskExecutor,要使用@Configuration和@EnableAsync這兩個(gè)注解,表示這是個(gè)配置類(lèi),并且是線(xiàn)程池的配置類(lèi),如下所示:
@Configuration @EnableAsync @Slf4j public class ExecutorConfig { @Bean public Executor asyncServiceExecutor() { log.info("start asyncServiceExecutor"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //配置核心線(xiàn)程數(shù) executor.setCorePoolSize(5); //配置最大線(xiàn)程數(shù) executor.setMaxPoolSize(5); //配置隊(duì)列大小 executor.setQueueCapacity(99999); //配置線(xiàn)程池中的線(xiàn)程的名稱(chēng)前綴 executor.setThreadNamePrefix("async-service-"); // rejection-policy:當(dāng)pool已經(jīng)達(dá)到max size的時(shí)候,如何處理新任務(wù) // CALLER_RUNS:不在新線(xiàn)程中執(zhí)行任務(wù),而是有調(diào)用者所在的線(xiàn)程來(lái)執(zhí)行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //執(zhí)行初始化 executor.initialize(); return executor; } }
注意,上面的方法名稱(chēng)為asyncServiceExecutor,稍后馬上用到;
將Service層的服務(wù)異步化
打開(kāi)AsyncServiceImpl.java,在executeAsync方法上增加注解@Async(“asyncServiceExecutor”),asyncServiceExecutor是前面ExecutorConfig.java中的方法名,表明executeAsync方法進(jìn)入的線(xià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)證效果
將這個(gè)springboot運(yùn)行起來(lái)(pom.xml所在文件夾下執(zhí)行mvn spring-boot:run);
在瀏覽器輸入:http://localhost:8080;
在瀏覽器用F5按鈕快速多刷新幾次;
在springboot的控制臺(tái)看見(jiàn)日志如下:
2019-08-12 15:23:00.320 INFO 5848 --- [nio-8080-exec-2] cn.ac.iie.controller.Hello : start submit 2019-08-12 15:23:00.327 INFO 5848 --- [nio-8080-exec-2] cn.ac.iie.controller.Hello : end submit 2019-08-12 15:23:00.327 INFO 5848 --- [async-service-1] cn.ac.iie.service.impl.AsyncServiceImpl : start executeAsync 2019-08-12 15:23:01.329 INFO 5848 --- [async-service-1] cn.ac.iie.service.impl.AsyncServiceImpl : end executeAsync 2019-08-12 15:24:17.449 INFO 5848 --- [nio-8080-exec-5] cn.ac.iie.controller.Hello : start submit 2019-08-12 15:24:17.450 INFO 5848 --- [nio-8080-exec-5] cn.ac.iie.controller.Hello : end submit 2019-08-12 15:24:17.450 INFO 5848 --- [async-service-2] cn.ac.iie.service.impl.AsyncServiceImpl : start executeAsync 2019-08-12 15:24:18.125 INFO 5848 --- [nio-8080-exec-6] cn.ac.iie.controller.Hello : start submit 2019-08-12 15:24:18.126 INFO 5848 --- [nio-8080-exec-6] cn.ac.iie.controller.Hello : end submit 2019-08-12 15:24:18.128 INFO 5848 --- [async-service-3] cn.ac.iie.service.impl.AsyncServiceImpl : start executeAsync 2019-08-12 15:24:18.451 INFO 5848 --- [async-service-2] cn.ac.iie.service.impl.AsyncServiceImpl : end executeAsync 2019-08-12 15:24:18.685 INFO 5848 --- [nio-8080-exec-7] cn.ac.iie.controller.Hello : start submit 2019-08-12 15:24:18.688 INFO 5848 --- [nio-8080-exec-7] cn.ac.iie.controller.Hello : end submit 2019-08-12 15:24:18.703 INFO 5848 --- [async-service-4] cn.ac.iie.service.impl.AsyncServiceImpl : start executeAsync 2019-08-12 15:24:19.130 INFO 5848 --- [async-service-3] cn.ac.iie.service.impl.AsyncServiceImpl : end executeAsync 2019-08-12 15:24:19.704 INFO 5848 --- [async-service-4] cn.ac.iie.service.impl.AsyncServiceImpl : end executeAsync
如上日志所示,我們可以看到controller的執(zhí)行線(xiàn)程是"nio-8080-exec-5",這是tomcat的執(zhí)行線(xiàn)程,而service層的日志顯示線(xiàn)程名為“async-service-1”,顯然已經(jīng)在我們配置的線(xiàn)程池中執(zhí)行了,并且每次請(qǐng)求中,controller的起始和結(jié)束日志都是連續(xù)打印的,表明每次請(qǐng)求都快速響應(yīng)了,而耗時(shí)的操作都留給線(xiàn)程池中的線(xiàn)程去異步執(zhí)行;
擴(kuò)展ThreadPoolTaskExecutor
雖然我們已經(jīng)用上了線(xiàn)程池,但是還不清楚線(xiàn)程池當(dāng)時(shí)的情況,有多少線(xiàn)程在執(zhí)行,多少在隊(duì)列中等待呢?這里我創(chuàng)建了一個(gè)ThreadPoolTaskExecutor的子類(lèi),在每次提交線(xiàn)程的時(shí)候都會(huì)將當(dāng)前線(xiàn)程池的運(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()); } @Override public void execute(Runnable task) { showThreadPoolInfo("1. do execute"); super.execute(task); } @Override public void execute(Runnable task, long startTimeout) { showThreadPoolInfo("2. do execute"); super.execute(task, startTimeout); } @Override public Future> submit(Runnable task) { showThreadPoolInfo("1. do submit"); return super.submit(task); } @Override publicFuture submit(Callable task) { showThreadPoolInfo("2. do submit"); return super.submit(task); } @Override public ListenableFuture> submitListenable(Runnable task) { showThreadPoolInfo("1. do submitListenable"); return super.submitListenable(task); } @Override public ListenableFuture submitListenable(Callable task) { showThreadPoolInfo("2. do submitListenable"); return super.submitListenable(task); } }
如上所示,showThreadPoolInfo方法中將任務(wù)總數(shù)、已完成數(shù)、活躍線(xiàn)程數(shù),隊(duì)列大小都打印出來(lái)了,然后Override了父類(lèi)的execute、submit等方法,在里面調(diào)用showThreadPoolInfo方法,這樣每次有任務(wù)被提交到線(xiàn)程池的時(shí)候,都會(huì)將當(dāng)前線(xiàn)程池的基本情況打印到日志中;
修改ExecutorConfig.java的asyncServiceExecutor方法,將ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor()改為T(mén)hreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor(),如下所示:
@Bean public Executor asyncServiceExecutor() { log.info("start asyncServiceExecutor"); ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor(); //配置核心線(xiàn)程數(shù) executor.setCorePoolSize(5); //配置最大線(xiàn)程數(shù) executor.setMaxPoolSize(5); //配置隊(duì)列大小 executor.setQueueCapacity(99999); //配置線(xiàn)程池中的線(xiàn)程的名稱(chēng)前綴 executor.setThreadNamePrefix("async-service-"); // rejection-policy:當(dāng)pool已經(jīng)達(dá)到max size的時(shí)候,如何處理新任務(wù) // CALLER_RUNS:不在新線(xiàn)程中執(zhí)行任務(wù),而是有調(diào)用者所在的線(xiàn)程來(lái)執(zhí)行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //執(zhí)行初始化 executor.initialize(); return executor; }
修改hello.java,方便查看線(xiàn)程池結(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-" }
看完上述內(nèi)容,你們對(duì)如何使用Springboot線(xiàn)程池有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。
新聞名稱(chēng):如何使用Springboot線(xiàn)程池
標(biāo)題路徑:http://www.ef60e0e.cn/article/gsideh.html