springboot 中異步調用 使用@Async

一、背景

對於一些的耗時的且與處理結果業務不是緊密關聯的,我們採用異步調用的方式處理。一般我們會手動創建一個線程池,來執行這個耗時的異步任務。其實spring 已經提供了一個註解來幫我們幹了這件事了

二、使用方式

使用方式就是很簡單了

1、在啓動類中加入@EnableAsync 是異步調用 @Async註解生效

2、在需要異步執行的方法上加上@Async,也可以在類上面加,表示該類中的所有的方法都是需要異步執行的

注意點:

1、默認情況情況下使用的是這個SimpleAsyncTaskExecutor 線程池,這個並不是真正意義的線程池,線程是不重用的,每個任務來都會創建一個新的線程。有可能導致OOM問題,所以建議自定義線程池,通過實現AsyncConfigurer 類,重寫getAsyncExecutor 方法。代碼如下:

@Slf4j
@Component
public class MyAsyncConfigurer implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ExecutorService service = Executors.newFixedThreadPool(10);
        return service;
    }

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

    class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {

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

}

2、調用的異步方法不能是同一個類的方法。原因是@Async註解本質上是使用的動態代理,spring在啓動掃描時,將含有AOP註解的類和對象替換成代理類,而同類調用時,還是調用的是對象本身,並不是代理類。 所以會導致@Async失效。同樣的問題@Transaction、@cache 也有。

解決這問題可以將異步的方法放到一個單獨的類中,這個類加上@compent 交給spring 管理。或者我們可以通過spring的上下文來獲取代理對象。詳細代碼:

@RestController
@RequestMapping("/api")
@Slf4j
public class ApiController {

    @Resource
    private ApplicationContext applicationContext;

    @RequestMapping("/asynCall")
    public Object asynCall() {

        try {
            ApiController apiController = applicationContext.getBean(ApiController.class);
            for (int i= 0;i<10;i++) {
                Future future = apiController.testAsynTask(i);
            }
            return "success";
        } catch (Exception e) {

            return "error";
        }


    }

    @Async
    public Future testAsynTask(int i ) throws InterruptedException {
        Thread.sleep(10000);
        System.out.println("異步任務執行完成..,"+i+","+Thread.currentThread().getName());
        Future  future = new AsyncResult("ok");
        return future;


    }}

 

 

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