SpringBoot中異步註解@Async的使用以及注意事項

第一步開啓異步:

@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }

默認情況下,@EnableAsync檢測Spring的@Async註釋和EJB 3.1 javax. EJB .異步;此選項還可用於檢測其他用戶定義的註釋類型。(也可以在SpringBoot的啓動類上直接加@EnableAsync註解)

在 Spring 中,用 @Async 註解指定的方法,該方法被調用時會以異步的方式執行。而如果沒有在 @Async 註解中指定線程池,就會使用默認的線程池。默認的線程池爲 SimpleAsyncTaskExecutor 。

該線程池不會複用線程,每有一個新任務被提交,該線程池就會創建一個新的線程實例用於執行任務。下面爲相關的代碼:

protected void doExecute(Runnable task) {
    Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
    thread.start();
}

而如果想要指定線程池,可以通過在 @Async 註解中的 value 參數中指定所要使用的線程池的 Bean Name 。另一種方法是是一個實現了 AsyncConfigurer 接口或是繼承其默認適配器類 AsyncConfigurerSupport 的配置類,這樣 @Async 註解的方法就會使用指定的自定義的線程池。

使用@Async註解的話採用的是springBoot默認的線程池,不過一般我們會自定義線程池(因爲比較靈活),配置方式有:

  1. 使用 xml 文件配置的方式

  2. 使用Java代碼結合@Configuration進行配置(推薦使用)

下面顯示配置線程的代碼實現

package com.deppon.ptos.load.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Description: 異步線程管理
 * @Author:   LYH
 * @CreateDate: 2019/6/27 8:54
 * @Version: 1.0
 * @JDK: 1.8
 */
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {


    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;

    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        //配置核心線程數
        executor.setCorePoolSize(corePoolSize);
        //配置最大線程數
        executor.setMaxPoolSize(maxPoolSize);
        //配置隊列大小
        executor.setQueueCapacity(queueCapacity);
        //配置線程池中的線程的名稱前綴
        executor.setThreadNamePrefix(namePrefix);

        // rejection-policy:當pool已經達到max size的時候,如何處理新任務
        // CALLER_RUNS:不在新線程中執行任務,而是有調用者所在的線程來執行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //執行初始化
        executor.initialize();
        return executor;
    }
}
package com.deppon.ptos.load.config;


import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Description: 打印異步線程的執行情況   使用Callbale Future 來返回線程的信息
 * @Author: 633805  LYH
 * @CreateDate: 2019/6/27 8:59
 * @Version: 1.0
 * @JDK: 1.8
 */
@Component
@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
    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);
    }
}

使用:

@Async("asyncServiceExecutor")

到這一步,異步就算開啓了。

下面主要說一說錯誤的使用@Async導致異步不成功的情況:

如下方式會使@Async失效

  • 異步方法使用static修飾

  • 異步類沒有使用@Component註解(或其他註解)導致spring無法掃描到異步類

  • 異步方法不能與被調用的異步方法在同一個類中

  • 類中需要使用@Autowired或@Resource等註解自動注入,不能自己手動new對象

  • 如果使用SpringBoot框架必須在啓動類中增加@EnableAsync註解

如果想更加詳細瞭解請參考:https://juejin.im/post/5d47a80a6fb9a06ad3470f9a

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章