天天看點

Spring Boot:異步任務與定時任務

測試代碼

​pom.xml​

​:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
    </parent>

    <packaging>jar</packaging>

    <groupId>com.kaven</groupId>
    <artifactId>springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>springboot</name>
    <description>springboot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>      

啟動類:

package com.kaven.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SpringbootApplication.class);
        application.run(args);
    }
}      

異步任務

在上下文中沒有執行器​

​bean​

​​的情況下,​

​Spring Boot​

​​會自動配置​

​ThreadPoolTaskExecutor​

​​,使其具有合理的預設值,這些預設值可以自動關聯到異步任務執行(​

​@EnableSync​

​​)和​

​Spring MVC​

​異步請求處理。

線程池預設使用​

​8​

​​個核心線程,它們可以根據負載進行動态擴充和收縮。可以使用​

​spring.task.execution​

​命名空間修改這些預設設定。

public static class Pool {

    /**
     * 隊列容量
     * Integer.MAX_VALUE表示無限容量(隊列無界),是以會忽略maxSize屬性
     */
    private int queueCapacity = Integer.MAX_VALUE;

    /**
     * 核心線程數
     */
    private int coreSize = 8;

    /**
     * 允許的最大線程數
     * 如果任務正在填滿隊列,則線程池可以擴充到該大小以适應負載
     * 如果隊列無界則忽略該屬性
     */
    private int maxSize = Integer.MAX_VALUE;

    /**
     * 是否允許核心線程逾時
     * 這可以實作線程池的動态擴充和收縮
     */
    private boolean allowCoreThreadTimeout = true;

    /**
     * 線程在終止之前可能保持空閑的時間限制
     */
    private Duration keepAlive = Duration.ofSeconds(60);
  }      

​application.properties​

​:

spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s      

上面的配置會将線程池更改為使用有界隊列,以便當隊列已滿(​

​100​

​​個任務)時,線程池增加到​

​16​

​​個線程。當線程空閑​

​10​

​​秒(而不是預設情況下的​

​60​

​秒)後會被回收。

開啟異步任務功能:

package com.kaven.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SpringbootApplication.class);
        application.run(args);
    }
}      

異步任務:

package com.kaven.springboot.task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class AsyncTask {
    @Async
    public void service(){
        try {
            // 模拟異步任務處理
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(System.currentTimeMillis());
        System.out.println("異步任務處理結束");
    }
}      

接口:

package com.kaven.springboot.controller;

import com.kaven.springboot.task.AsyncTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class TaskController {

    @Resource
    private AsyncTask asyncTask;
    
    @GetMapping("/async")
    public long async() {
        asyncTask.service();
        return System.currentTimeMillis();
    }
}      

使用​

​Postman​

​進行測試。

Spring Boot:異步任務與定時任務
1644482662434      
Spring Boot:異步任務與定時任務
1644482667447
異步任務處理結束      

很顯然請求響應時​

​AsyncTask​

​​類的​

​service​

​方法還沒有執行完,符合異步任務的預期。

定時任務

如果需要與定時任務的執行相關聯(例如使用​

​@EnableScheduling​

​​),​

​Spring Boot​

​​會自動配置​

​ThreadPoolTaskScheduler​

​​。線程池預設使用一個線程,可以使用​

​spring.task.scheduling​

​命名空間修改其設定。

​application.properties​

​:

spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2      
  • ​spring.task.scheduling.thread-name-prefix​

    ​:用于建立新線程的名稱字首。
  • ​spring.task.scheduling.pool.size​

    ​:允許的最大線程數。

開啟定時任務功能:

package com.kaven.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringbootApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SpringbootApplication.class);
        application.run(args);
    }
}      

定時任務:

package com.kaven.springboot.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduleTask {
    @Scheduled(cron = "*/10 * * * * *")
    public void service(){
        System.out.println(System.currentTimeMillis());
        System.out.println("定時任務處理結束");
    }
}      

​*/10 * * * * *​

​​表示每​

​10​

​​秒鐘執行一次任務,​

​cron​

​時間表達式的介紹可以參考以下部落格:

  • ​​cron時間表達式簡介​​
  • ​​Cron表達式詳解​​

接口:

package com.kaven.springboot.controller;

import com.kaven.springboot.task.ScheduleTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class TaskController {

    @Resource
    private ScheduleTask scheduleTask;

    @GetMapping("/schedule")
    public String schedule() {
        scheduleTask.service();
        return "定時任務啟動成功";
    }
}      

使用​

​Postman​

​進行測試。

Spring Boot:異步任務與定時任務

控制台輸出如下圖所示:

Spring Boot:異步任務與定時任務