Spring Boot Async異步執行任務

Spring Boot Async異步執行任務

異步調用就是不用等待結果的返回就執行後面的邏輯,同步調用則須要等帶結果再執行後面的邏輯。java

一般咱們使用異步操做都會去建立一個線程執行一段邏輯,而後把這個線程丟到線程池中去執行,代碼以下:面試

ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(() -> {
    try {
        // 業務邏輯
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
    }
 });

這樣的方式看起來沒那麼優雅,儘管用了java的lambda。在Spring Boot中有一種更簡單的方式來執行異步操做,只須要一個@Async註解便可。spring

@Async
public void saveLog() {
    System.err.println(Thread.currentThread().getName());
}

咱們能夠直接在Controller中調用這個業務方法,它就是異步執行的,會在默認的線程池中去執行。須要注意的是必定要在外部的類中去調用這個方法,若是在本類調用是不起做用的,好比this.saveLog()。 最後在啓動類上開啓異步任務的執行,添加@EnableAsync便可。緩存

另外關於執行異步任務的線程池咱們也能夠自定義,首先咱們定義一個線程池的配置類,用來配置一些參數,具體代碼以下:微信

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;  
/**
 * 異步任務線程池配置
 * 
 * @author yinjihuan
 */
@Configuration
@ConfigurationProperties(prefix = "spring.task.pool")
public class TaskThreadPoolConfig { 
    //核心線程數
    private int corePoolSize = 5;  
    //最大線程數
    private int maxPoolSize = 50;  
    //線程池維護線程所容許的空閒時間
    private int keepAliveSeconds = 60;  
    //隊列長度
    private int queueCapacity = 10000;
    //線程名稱前綴
    private String threadNamePrefix = "FSH-AsyncTask-";
    public String getThreadNamePrefix() {
        return threadNamePrefix;
    }
    public void setThreadNamePrefix(String threadNamePrefix) {
        this.threadNamePrefix = threadNamePrefix;
    }
    public int getCorePoolSize() {
        return corePoolSize;
    }
    public void setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
    }
    public int getMaxPoolSize() {
        return maxPoolSize;
    }
    public void setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }
    public int getKeepAliveSeconds() {
        return keepAliveSeconds;
    }
    public void setKeepAliveSeconds(int keepAliveSeconds) {
        this.keepAliveSeconds = keepAliveSeconds;
    }
    public int getQueueCapacity() {
        return queueCapacity;
    }
    public void setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
    }
}

而後咱們從新定義線程池的配置:異步

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration  
public class AsyncTaskExecutePool implements AsyncConfigurer {    
    private Logger logger = LoggerFactory.getLogger(AsyncTaskExecutePool.class);

    @Autowired    
    private TaskThreadPoolConfig config;

    @Override  
    public Executor getAsyncExecutor() {  
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
        executor.setCorePoolSize(config.getCorePoolSize());    
        executor.setMaxPoolSize(config.getMaxPoolSize());    
        executor.setQueueCapacity(config.getQueueCapacity());    
        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());    
        executor.setThreadNamePrefix(config.getThreadNamePrefix());
        //線程池對拒絕任務(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy
        //AbortPolicy:直接拋出java.util.concurrent.RejectedExecutionException異常 -->
        //CallerRunsPolicy:主線程直接執行該任務,執行完以後嘗試添加下一個任務到線程池中,能夠有效下降向線程池內添加任務的速度 -->
        //DiscardOldestPolicy:拋棄舊的任務、暫不支持;會致使被丟棄的任務沒法再次被執行 -->
        //DiscardPolicy:拋棄當前任務、暫不支持;會致使被丟棄的任務沒法再次被執行 -->
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
        executor.initialize();    
        return executor;    
    }  
    @Override  
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {// 異步任務中異常處理  
        return new AsyncUncaughtExceptionHandler() {  
            @Override  
            public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) {  
                logger.error("=========================="+arg0.getMessage()+"=======================", arg0);  
                logger.error("exception method:" + arg1.getName());  
            }  
        };  
    }    
}

配置完以後咱們的異步任務執行的線程池就是咱們自定義的了,咱們能夠經過在屬性文件裏面配置線程池的大小等等信息,也可使用默認的配置:ide

spring.task.pool.maxPoolSize=100

最後講下線程池配置的拒絕策略,當咱們的線程數量高於線程池的處理速度時,任務會被緩存到本地的隊列中,隊列也是有大小的,若是超過了這個大小,咱們須要有拒絕的策略,否則就會內存溢出了,目前支持2種拒絕策略:this

  • AbortPolicy: 直接拋出java.util.concurrent.RejectedExecutionException異常
  • CallerRunsPolicy: 主線程直接執行該任務,執行完以後嘗試添加下一個任務到線程池中,能夠有效下降向線程池內添加任務的速度
    建議你們用CallerRunsPolicy策略,由於當隊列中的任務滿了以後,若是直接拋異常,那麼這個任務就會被丟棄,若是是CallerRunsPolicy策略會用主線程去執行,就是同步執行,最起碼這樣任務不會丟棄。

推薦相關閱讀:線程

  • 《反射面試題-請了解下》code

  • 《註解面試題-請了解下》

更多技術分享請加我微信,我拉你進羣進行交流:
Spring Boot Async異步執行任務

相關文章
相關標籤/搜索