springboot使用@async實現異步線程池


一 介紹

工作中經常涉及異步任務,通常是使用多線程技術,比如線程池ThreadPoolExecutor,但使用Executors容易產生OOM,需要手動使用ThreadPoolExecutor創建線程池;在springboot使用 @async 可以實現異步調用,配置線程池參數,可以簡單的實現多線程的線程池效果,從而簡化開發,避免OOM;

二 異步調用

2.1無返回異步

我們知道同步執行就是按照代碼的順序執行,而異步執行則是無序,在springboot中使用實現異步調用函數非常簡單,首先在啟動類上加上@EnableAsync 注解;

/**
 * @Author lsc
 * <p> </p>
 */
@SpringBootApplication
@EnableAsync
public class AsyncRunApp {

    public static void main(String[] args) {
        SpringApplication.run(AsyncRunApp.class, args);
    }
}

其次,在函數上標上@sync注解,表示異步調用

    @Async
    public void taskOne() throws Exception {
        System.out.println("任務一");

    }

    @Async
    public void taskTwo() throws Exception {
        System.out.println("任務二");
    }

測試代碼

    @Autowired
    Task task;

    @Test
    public void test() throws Exception {
        task.taskOne();
        task.taskTwo();
    }

如果按照同步執行邏輯會先執行任務一,然后再執行任務二,如果是異步執行,則無序,可能任務一先執行,也可能任務二先執行;

2.2 有返回值回調

有時候要知道任務是否執行完成,再繼續做其它的業務邏輯,就需要使用到Future接口,其含義是在執行異步任務后會給一個回調函數,我們只要設置回調信息,就可以知道任務是否正確執行完成;我們對異步函數,添加 Future 返回值類型,使用 new AsyncResult<>() 設置回調信息;

@Component
public class Task {

    @Async
    public Future<String> taskOne() throws Exception {
        System.out.println("任務一");
        return new AsyncResult<>("任務一執行完成");

    }

    @Async
    public Future<String> taskTwo() throws Exception {
        System.out.println("任務二");
        return new AsyncResult<>("任務二執行完成");
    }
}

測試代碼如下, 等待2個任務全部完成后就打印出返回值信息

 @Autowired
    Task task;

    @Test
    public void test() throws Exception {
        Future<String> str1 = task.taskOne();
        Future<String> str2 = task.taskTwo();
        while (true){
            // 如果任務都做完就執行如下邏輯
            if (str1.isDone() && str2.isDone()){
                System.out.println(str1.get()+":"+str2.get());
                break;
            }
        }
    }

執行輸出

任務二
任務一
任務一執行完成:任務二執行完成

關注公眾號 知識追尋者 獲取原創PDF,面試題集,最新成熟技術棧

三 線程池

在異步掉用中使用的@Async 注解,默認的線程池大小如下;

# 核心線程數
spring.task.execution.pool.core-size=8  
# 最大線程數
spring.task.execution.pool.max-size=16
# 空閑線程存活時間
spring.task.execution.pool.keep-alive=60s
# 是否允許核心線程超時
spring.task.execution.pool.allow-core-thread-timeout=true
# 線程隊列數量
spring.task.execution.pool.queue-capacity=100
# 線程關閉等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# 線程名稱前綴
spring.task.execution.thread-name-prefix=task-

一般情況下,我們都需要手動創建線程池,使用 ThreadPoolTaskExecutor 類進行配置;這邊設置了線程前綴名稱,等下測試時就可以判定是否線程池配置成功;

@Configuration
public class PoolConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 設置核心線程數
        executor.setCorePoolSize(10);
        // 設置最大線程數
        executor.setMaxPoolSize(15);
        // 設置隊列容量
        executor.setQueueCapacity(20);
        // 設置線程活躍時間(秒)
        executor.setKeepAliveSeconds(60);
        // 設置默認線程名稱
        executor.setThreadNamePrefix("zszxz-");
        // 設置拒絕策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任務結束后再關閉線程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

在task類中加上 新的一個方法如下

    @Async
    public void sayHello(String name) {
        LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
    }

使用測試類進行測試

   @Test
    public void testPool() throws Exception {
        task.sayHello("公眾號:知識追尋者");
    }

執行結果如下,日志打印出線程名稱為zszxz-1;

有時候,一個項目中如果配置了多個線程池,如下格式

	@Bean("pool1")
    public TaskExecutor taskExecutor1() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //....
        return executor;
    }
    @Bean("pool2")
    public TaskExecutor taskExecutor2() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //....
        return executor;
    }
    @Bean("pool3")
    public TaskExecutor taskExecutor3() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //....
        return executor;
    }

在使用 @Async注解時就需要指明具體使用的線程池,如下格式

    @Async("pool1")
    public void sayHello1(String name) {
        LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
    }
    
     @Async("pool2")
    public void sayHello1(String name) {
        LoggerFactory.getLogger(Task.class).info(name + ":Hello World!");
    }

本套教程

源碼地址:關注公眾號知識追尋者 回復 springboot


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM