Spring @Async開啟非同步任務

sisibeloved發表於2018-03-31

1. 開啟非同步

@SpringBootApplication
@EnableAsync        //開啟非同步任務
public class Application {
    @Bean(name="processExecutor")
    public TaskExecutor workExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("Async-");
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(20);
        threadPoolTaskExecutor.setQueueCapacity(600);
        threadPoolTaskExecutor.afterPropertiesSet();
        
        // 自定義拒絕策略
        threadPoolTaskExecutor.setRejectedExecutionHandler((r, executor) -> {
            // .....
        });
        // 使用預設的拒絕策略
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        
        return threadPoolTaskExecutor;
    }




    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
複製程式碼

2. 配置執行緒池

Spring非同步執行緒池的介面類,其實質是java.util.concurrent.Executor。

Spring 已經實現的異常執行緒池:

  1. SimpleAsyncTaskExecutor:
    不是真的執行緒池,這個類不重用執行緒,每次呼叫都會建立一個新的執行緒。
  2. SyncTaskExecutor:
    這個類沒有實現非同步呼叫,只是一個同步操作。只適用於不需要多執行緒的地方
  3. ConcurrentTaskExecutor:
    Executor的適配類,不推薦使用。如果ThreadPoolTaskExecutor不滿足要求時,才用考慮使用這個類
  4. SimpleThreadPoolTaskExecutor:
    是Quartz的SimpleThreadPool的類。執行緒池同時被quartz和非quartz使用,才需要使用此類
  5. ThreadPoolTaskExecutor:
    最常使用,推薦。其實質是對java.util.concurrent.ThreadPoolExecutor的包裝。

3. 新增@Async註解

    /**
     * 非同步呼叫返回Future
     * 
     * @param i
     * @return
     */
    @Async
    public Future<String> asyncInvokeReturnFuture(int i) {
        log.info("asyncInvokeReturnFuture, parementer={}", i);
        Future<String> future;
        try {
            Thread.sleep(1000 * 1);
            future = new AsyncResult<String>("success:" + i);
        } catch (InterruptedException e) {
            future = new AsyncResult<String>("error");
        }
        return future;
    }
複製程式碼

4. 通過XML檔案配置

    <!-- 等價於 @EnableAsync, executor指定執行緒池 -->
    <task:annotation-driven executor="xmlExecutor"/>
    <!-- id指定執行緒池產生執行緒名稱的字首 -->
    <task:executor
        id="xmlExecutor"
        pool-size="5-25"
        queue-capacity="100"
        keep-alive="120"
        rejection-policy="CALLER_RUNS"/>
複製程式碼

5. 異常處理

在呼叫方法時,可能出現方法中丟擲異常的情況。在非同步中主要有有兩種異常處理方法:

  1. 對於方法返回值是Futrue的非同步方法:
    a) 一種是在呼叫future的get時捕獲異常;
    b) 在異常方法中直接捕獲異常
  2. 對於返回值是void的非同步方法:
    通過AsyncUncaughtExceptionHandler處理異常
/**
 * 通過實現AsyncConfigurer自定義執行緒池,包含異常處理
 *
 */
@Service
public class MyAsyncConfigurer implements AsyncConfigurer{
    private static final Logger log = LoggerFactory.getLogger(MyAsyncConfigurer.class);

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();  
        threadPool.setCorePoolSize(1);  
        threadPool.setMaxPoolSize(1);  
        threadPool.setWaitForTasksToCompleteOnShutdown(true);  
        threadPool.setAwaitTerminationSeconds(60 * 15);  
        threadPool.setThreadNamePrefix("MyAsync-");
        threadPool.initialize();
        return threadPool;  
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return new MyAsyncExceptionHandler();  
    }

    /**
     * 自定義異常處理類
     */
    class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {  

        @Override  
        public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {  
            log.info("Exception message - " + throwable.getMessage());  
            log.info("Method name - " + method.getName());  
            for (Object param : obj) {  
                log.info("Parameter value - " + param);  
            }  
        }  

    } 

}
複製程式碼

6. 參考文章

[1] Spring Boot系列二 Spring @Async非同步執行緒池用法總結

[2] Spring Boot開啟非同步任務 子執行緒

相關文章