Spring中異步方法的使用

Spring中異步方法的使用

1. 異步方法描述

異步方法,顧名思義就是調用後無須等待它的執行,而繼續往下執行;@Async是Spring的一個註解,在Spring Boot中,我們只需要使用@Async註解就能簡單的將原來的同步函數變爲異步函數。

對於比較耗時的操作,我們可以抽取成異步方法來讓主線程穩定快速繼續執行,對於異步方法的執行結果可根據自己的要求是否需要在主線程處理;

2. 異步方法的實現步驟

在springboot應用我們可以使用簡單的兩個註解即可開啓並使用異步方法;

  1. 創建一個普通的Service類,並有@Service修飾,表示這個服務類交給Spring管理;
  2. 在Service方法裏定義一個普通的方法,使用@Async修飾;表示這是一個異步方法;
  3. 在引導類中添加@EnableAsync註解,使應用開啓對異步方法的支持;

3. 實測一下

3.1 定義個Service類

@Slf4j
@Service
public class MyAsyncSevice {

    @Async
    public void myAsyncMehtod(){
        log.info("---> enter aysnc method");
        try {
            Thread.sleep(5000);
            int i = 1/0;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("---> end of async method");
    }
}

3.2 定義個測試Controlle

@Slf4j
@RestController
public class AsyncContorller {

    // 注入異步方法所在的類
    @Autowired
    MyAsyncSevice myAsyncSevice;

    @GetMapping("/async-test")
    public String asyncTest(){
        log.info("--> enter controller");
        myAsyncSevice.myAsyncMehtod();
        log.info("--> end of controller");
        return "hello";
    }
}

3.3 在啓動類上啓用異步

@EnableAsync
@SpringBootApplication
public class JsrDemoApplication {

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

}

3.4 測試

可以看到輸出,controlle中調用了異步方法後繼續執行,即使異步方法報錯也不會controller的執行;

curl -X GET -H   "http://localhost:8080/async-test"
2021-08-20 17:42:50.810   --- [nio-8080-exec-1] AsyncContorller  : --> enter controller
2021-08-20 17:42:50.813   --- [nio-8080-exec-1] AsyncContorller  : --> end of controller
2021-08-20 17:42:50.821   --- [         task-1] MyAsyncSevice  : ---> enter aysnc method
2021-08-20 17:42:55.832   --- [         task-1] MyAsyncSevice  : ---> end of async method

4. 補充

4.1 Executor線程池

上面的測試,我們並沒有創建新的線程和線程池,如果我們不配置線程池的Bean,Spring會自動創建SimpleAsyncTaskExecutor,並使用它來執行異步方法。定義線程池bean可參考如下:

@Bean
public Executor taskExecutor() {
 	ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 	executor.setCorePoolSize(3);
 	executor.setMaxPoolSize(3);
 	executor.setQueueCapacity(500);
 	executor.setThreadNamePrefix("MyThreadPool");
 	executor.initialize();
 	return executor;
}

4.2 異步返回Futrue

4.2.1 這樣改造我們的異步方法,即返回一個Futrue類型的結果;

    @Async
    public Future myAsyncMehtod(){
        log.info("---> enter aysnc method");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("---> end of async method");

        return new AsyncResult("I am async method result");
    }

4.2.2 Contorller也改造下

使用了Futrue.get方法,controller主線程就會等待異步方法的執行結束,或等待超時後纔會結束。

	@GetMapping("/async-test")
    public String asyncTest(){
        log.info("--> enter controller");
        Future ft = myAsyncSevice.myAsyncMehtod();
        try {
            ft.get(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        log.info("--> end of controller");
        return "hello";
    }

4.3 其他注意事項

  • 在同一個類中的方法調用,添加@async註解是失效的。原因是當你在同一個類中的時候,方法調用是在類中執行的,spring無法截獲這個方法調用,也就不會在代理類裏執行。
  • 可能會導致循環依賴,spring本身會解決循環依賴,但是因爲@Async使用代理模式,spring在檢查第二級緩存和原始對象是否相等時發現不相等,會拋出異常。
  • 無法獲取請求上下文。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章