欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入學(xué)習(xí)springboot線程池的使用和擴(kuò)展

 更新時(shí)間:2019年06月25日 14:27:07   作者:程序員欣宸  
這篇文章主要介紹了深入學(xué)習(xí)springboot線程池的使用和擴(kuò)展,springboot框架提供了@Async注解,幫助我們更方便的將業(yè)務(wù)邏輯提交到線程池中異步執(zhí)行,需要的朋友可以參考下

前言

我們常用ThreadPoolExecutor提供的線程池服務(wù),springboot框架提供了@Async注解,幫助我們更方便的將業(yè)務(wù)邏輯提交到線程池中異步執(zhí)行,今天我們就來(lái)實(shí)戰(zhàn)體驗(yàn)這個(gè)線程池服務(wù);

實(shí)戰(zhàn)環(huán)境

  • windowns10;
  • jdk1.8;
  • springboot 1.5.9.RELEASE;
  • 開(kāi)發(fā)工具:IntelliJ IDEA;

實(shí)戰(zhàn)源碼

本次實(shí)戰(zhàn)的源碼可以在我的GitHub下載,地址:git@github.com:zq2599/blog_demos.git,項(xiàng)目主頁(yè):

這里面有多個(gè)工程,本次用到的工程為threadpooldemoserver

實(shí)戰(zhàn)步驟梳理

本次實(shí)戰(zhàn)的步驟如下:

  1. 創(chuàng)建springboot工程;
  2. 創(chuàng)建Service層的接口和實(shí)現(xiàn);
  3. 創(chuàng)建controller,開(kāi)發(fā)一個(gè)http服務(wù)接口,里面會(huì)調(diào)用service層的服務(wù);
  4. 創(chuàng)建線程池的配置;
  5. 將Service層的服務(wù)異步化,這樣每次調(diào)用都會(huì)都被提交到線程池異步執(zhí)行;
  6. 擴(kuò)展ThreadPoolTaskExecutor,在提交任務(wù)到線程池的時(shí)候可以觀察到當(dāng)前線程池的情況;

創(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.bolingcavalry</groupId>
<artifactId>threadpooldemoserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>threadpooldemoserver</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</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
public class AsyncServiceImpl implements AsyncService {
private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);
@Override
public void executeAsync() {
logger.info("start executeAsync");
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
logger.info("end executeAsync");
}
}

這個(gè)方法做的事情很簡(jiǎn)單:sleep了一秒鐘;

創(chuàng)建controller

創(chuàng)建一個(gè)controller為Hello,里面定義一個(gè)http接口,做的事情是調(diào)用Service層的服務(wù),如下:

@RestController
public class Hello {
private static final Logger logger = LoggerFactory.getLogger(Hello.class);
@Autowired
private AsyncService asyncService;
@RequestMapping("/")
public String submit(){
logger.info("start submit");
//調(diào)用service層的任務(wù)
asyncService.executeAsync();
logger.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
public class ExecutorConfig {
private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
@Bean
public Executor asyncServiceExecutor() {
logger.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)建的,如下:

@Override
@Async("asyncServiceExecutor")
public void executeAsync() {
logger.info("start executeAsync");
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
logger.info("end executeAsync");
}

驗(yàn)證效果

  1. 將這個(gè)springboot運(yùn)行起來(lái)(pom.xml所在文件夾下執(zhí)行mvn spring-boot:run);
  2. 在瀏覽器輸入:http://localhost:8080;
  3. 在瀏覽器用F5按鈕快速多刷新幾次;
  4. 在springboot的控制臺(tái)看見(jiàn)日志如下:
2018-01-21 22:43:18.630 INFO 14824 --- [nio-8080-exec-8] c.b.t.controller.Hello : start submit
2018-01-21 22:43:18.630 INFO 14824 --- [nio-8080-exec-8] c.b.t.controller.Hello : end submit
2018-01-21 22:43:18.929 INFO 14824 --- [async-service-1] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 22:43:18.930 INFO 14824 --- [async-service-1] c.b.t.service.impl.AsyncServiceImpl : start executeAsync
2018-01-21 22:43:19.005 INFO 14824 --- [async-service-2] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 22:43:19.006 INFO 14824 --- [async-service-2] c.b.t.service.impl.AsyncServiceImpl : start executeAsync
2018-01-21 22:43:19.175 INFO 14824 --- [async-service-3] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 22:43:19.175 INFO 14824 --- [async-service-3] c.b.t.service.impl.AsyncServiceImpl : start executeAsync
2018-01-21 22:43:19.326 INFO 14824 --- [async-service-4] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 22:43:19.495 INFO 14824 --- [async-service-5] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 22:43:19.930 INFO 14824 --- [async-service-1] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 22:43:20.006 INFO 14824 --- [async-service-2] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 22:43:20.191 INFO 14824 --- [async-service-3] c.b.t.service.impl.AsyncServiceImpl : end executeAsync

如上日志所示,我們可以看到controller的執(zhí)行線程是"nio-8080-exec-8",這是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),代碼如下:

public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
private void showThreadPoolInfo(String prefix){
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if(null==threadPoolExecutor){
return;
}
logger.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
public <T> Future<T> submit(Callable<T> 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 <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(),如下所示:

@Bean
public Executor asyncServiceExecutor() {
logger.info("start asyncServiceExecutor");
//使用VisiableThreadPoolTaskExecutor
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;
}

再次啟動(dòng)該工程,再瀏覽器反復(fù)刷新http://localhost:8080,看到的日志如下:

2018-01-21 23:04:56.113 INFO 15580 --- [nio-8080-exec-1] c.b.t.e.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [99], completedTaskCount [85], activeCount [5], queueSize [9]
2018-01-21 23:04:56.113 INFO 15580 --- [nio-8080-exec-1] c.b.t.controller.Hello : end submit
2018-01-21 23:04:56.225 INFO 15580 --- [async-service-1] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 23:04:56.225 INFO 15580 --- [async-service-1] c.b.t.service.impl.AsyncServiceImpl : start executeAsync
2018-01-21 23:04:56.240 INFO 15580 --- [nio-8080-exec-2] c.b.t.controller.Hello : start submit
2018-01-21 23:04:56.240 INFO 15580 --- [nio-8080-exec-2] c.b.t.e.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [100], completedTaskCount [86], activeCount [5], queueSize [9]
2018-01-21 23:04:56.240 INFO 15580 --- [nio-8080-exec-2] c.b.t.controller.Hello : end submit
2018-01-21 23:04:56.298 INFO 15580 --- [async-service-2] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 23:04:56.298 INFO 15580 --- [async-service-2] c.b.t.service.impl.AsyncServiceImpl : start executeAsync
2018-01-21 23:04:56.372 INFO 15580 --- [nio-8080-exec-3] c.b.t.controller.Hello : start submit
2018-01-21 23:04:56.373 INFO 15580 --- [nio-8080-exec-3] c.b.t.e.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [101], completedTaskCount [87], activeCount [5], queueSize [9]
2018-01-21 23:04:56.373 INFO 15580 --- [nio-8080-exec-3] c.b.t.controller.Hello : end submit
2018-01-21 23:04:56.444 INFO 15580 --- [async-service-3] c.b.t.service.impl.AsyncServiceImpl : end executeAsync
2018-01-21 23:04:56.445 INFO 15580 --- [async-service-3] c.b.t.service.impl.AsyncServiceImpl : start executeAsync

注意這一行日志:2. do submit,taskCount [101], completedTaskCount [87], activeCount [5], queueSize [9]

這說(shuō)明提交任務(wù)到線程池的時(shí)候,調(diào)用的是submit(Callable task)這個(gè)方法,當(dāng)前已經(jīng)提交了101個(gè)任務(wù),完成了87個(gè),當(dāng)前有5個(gè)線程在處理任務(wù),還剩9個(gè)任務(wù)在隊(duì)列中等待,線程池的基本情況一路了然;

至此,springboot線程池服務(wù)的實(shí)戰(zhàn)就完成了,希望能幫您在工程中快速實(shí)現(xiàn)異步服務(wù);

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java類的加載連接和初始化實(shí)例分析

    Java類的加載連接和初始化實(shí)例分析

    這篇文章主要介紹了Java類的加載連接和初始化,結(jié)合具體實(shí)例形式分析了java類的加載、連接、初始化相關(guān)原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-07-07
  • JavaWeb如何實(shí)現(xiàn)統(tǒng)一查詢接口(jfinal)

    JavaWeb如何實(shí)現(xiàn)統(tǒng)一查詢接口(jfinal)

    這篇文章主要介紹了JavaWeb如何實(shí)現(xiàn)統(tǒng)一查詢接口(jfinal),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • centos7安裝java的多種方式總結(jié)

    centos7安裝java的多種方式總結(jié)

    這篇文章主要給大家介紹了關(guān)于centos7安裝java的多種方式,文中通過(guò)實(shí)例代碼以及圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-01-01
  • java可以作為第一門編程語(yǔ)言學(xué)習(xí)嗎

    java可以作為第一門編程語(yǔ)言學(xué)習(xí)嗎

    在本篇內(nèi)容里小編給JAVA零基礎(chǔ)的網(wǎng)友分享一篇關(guān)于java可以作為第一門編程語(yǔ)言學(xué)習(xí)嗎的文章,有興趣的朋友們可以參考下。
    2020-11-11
  • Java中Integer.valueOf,parsetInt() String.valueOf的區(qū)別和結(jié)果代碼解析

    Java中Integer.valueOf,parsetInt() String.valueOf的區(qū)別和結(jié)果代碼解析

    本文通過(guò)代碼給大家講解了JAVA中Integer.valueOf, parsetInt() String.valueOf的區(qū)別和結(jié)果,需要的朋友可以參考下
    2018-05-05
  • Springboot實(shí)現(xiàn)公共字段填充的示例詳解

    Springboot實(shí)現(xiàn)公共字段填充的示例詳解

    這篇文章主要為大家詳細(xì)介紹了Springboot實(shí)現(xiàn)公共字段填充的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),對(duì)我們深入學(xué)習(xí)springboot有一定的幫助,感興趣的小伙伴可以了解下
    2023-10-10
  • Java數(shù)組的擴(kuò)容代碼示例

    Java數(shù)組的擴(kuò)容代碼示例

    這篇文章主要介紹了Java數(shù)組的擴(kuò)容,分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2017-09-09
  • java實(shí)現(xiàn)簡(jiǎn)單的拼圖游戲

    java實(shí)現(xiàn)簡(jiǎn)單的拼圖游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單的拼圖游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Spring Boot中使用JSR-303實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn)

    Spring Boot中使用JSR-303實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn)

    這篇文章主要介紹了Spring Boot中使用JSR-303實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn),JSR-303校驗(yàn)我們一般都是對(duì)Java的實(shí)體類對(duì)象進(jìn)行校驗(yàn),主要檢驗(yàn)JSR-303是Java中的一個(gè)規(guī)范,用于實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn)在我們的實(shí)體類對(duì)象的屬性上,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • SpringSecurity?用戶帳號(hào)已被鎖定的問(wèn)題及解決方法

    SpringSecurity?用戶帳號(hào)已被鎖定的問(wèn)題及解決方法

    這篇文章主要介紹了SpringSecurity?用戶帳號(hào)已被鎖定,本文給大家分享問(wèn)題原因及解決方式,需要的朋友可以參考下
    2023-12-12

最新評(píng)論