SpringBoot中Async異步方法和定時任務介紹

1.功能說明

Spring提供了Async註解來實現方法的異步調用。 即當調用Async標識的方法時,調用線程不會等待被調用方法執行完成即返回繼續執行如下操做,而被調用的方法則會啓動一個獨立線程來執行此方法異步

這種異步執行的方式一般用於處理接口中不須要返回給用戶的數據處理。好比當註冊的時候,只須要將用戶信息返回用戶,而關於信息的保存操做可使用異步執行。async

Spring提供了Scheduled註解來實現定時任務的功能。ide

在異步方法和定時任務功能中都是開發這本身定義須要執行的方法,而後交給Spring容器管理線程,並執行相應的方法。在使用異步方法和定時任務的時候須要特別注意的是線程池的配置以及任務中異常的處理。下面對這兩個功能進行簡單介紹。單元測試

2.關鍵註解和配置接口

功能開啓註解:測試

EnableAsync和EnableScheduling

經過在Spring的配置類中添加這兩個註解來開啓Spring的異步方法和定時任務的功能spa

異步方法標識註解Async,其定義爲:線程

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {

	String value() default "";

}

在註解定義中能夠看到此註解能夠用於type和method,當此註解用於類的時候,表示此類中的全部方法都爲異步方法。此註解中的value屬性可用於指定執行此異步方法的線程池。線程池的具體肯定方法下面具體分析。3d

定時任務標識註解Scheduled,定義以下:日誌

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
    定時任務
	String cron() default "";

	String zone() default "";
    //上次執行結束到下次執行開始
	long fixedDelay() default -1;
	
	String fixedDelayString() default "";
    上次執行開始到本次執行開始
	long fixedRate() default -1;

    fixedRate或者fixedDelay的時候第一次的延遲時間
	long initialDelay() default -1;

}

3.Spring線程池的選擇和自定義配置線程池

在項目中咱們一般不會本身手動建立線程,而是經過統一的線程池來執行task或者異步方法,使用這種方法來避免多人團隊中因爲自定義線程致使的資源耗盡的問題。在自定義線程池以前首先要了解Spring在執行異步任務或者方法的時候是怎麼選擇線程池的。code

3.1 Async對於線程池的選擇順序

Async線程池的選擇順序以下圖所示:

Spring在執行async標識的異步方法的時候首先會在Spring的上下文中搜索類型爲TaskExecutor或者名稱爲「taskExecutor」的bean,當能夠找到的時候,就將任務提交到此線程池中執行。當不存在以上線程池的時候,Spring會手動建立一個SimpleAsyncTaskExecutor執行異步任務。

另外當標識async註解的時候指定了,value值,Spring會使用指定的線程池執行。好比如下:

@Async(value = "asyncTaskThreadPool")

這個時候Spring會去上下文中找名字爲asyncTaskThreadPool的bean,並執行異步任務,找不到,會拋出異常。

3.2 Scheduled對於線程池的選擇順序

Scheduled對於線程池的選擇順序以下圖所示:

當Spring執行定時任務的時候,首先會在上下文中找類型爲TaskScheduler或者名稱爲taskScheduler的bean,找不到的時候會手動建立一個線程執行此task。

3.3 自定義線程池和異常處理

在瞭解了Spring對於線程池的選擇後,咱們須要自定義線程池。自定義Async線程池有三種方式。

方法一:首先配置接口,重寫獲取線程池的方法。

配置Async方法的線程池須要繼承AsyncConfigurerSupport類,或者實現AsyncConfigurer接口,並重寫getAsyncExecutor方法,代碼以下:

@Configuration
@EnableAsync
public class ThreadPoolBeanFactory extends AsyncConfigurerSupport{

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor asyncTaskThreadPool = new ThreadPoolTaskExecutor();
        asyncTaskThreadPool.setCorePoolSize(100);
        asyncTaskThreadPool.setMaxPoolSize(200);
        asyncTaskThreadPool.setQueueCapacity(11);
        asyncTaskThreadPool.setThreadFactory(new ThreadFactory() {

            private final AtomicLong index = new AtomicLong(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Async-override-task-pool-thread-" + index.getAndIncrement());
            }
        });
        asyncTaskThreadPool.initialize();
        return asyncTaskThreadPool;
    }


    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        //返回值爲void的異步方法不會傳遞異常,當方法中出現異常的時候只會打印日誌,重寫此方法來自定義異常處理方法
        return null;
    }
}

這種定義的方法缺點是沒有定義bean。

方法二:自定義相應類型的線程池bean。

第二種方法是基於Spring對線程選擇的原理來實現的,定義一個類型爲TaskExecutor的bean,定義方式以下:

@Bean
    public TaskExecutor asyncTaskThreadPool() {

        ThreadPoolTaskExecutor asyncTaskThreadPool = new ThreadPoolTaskExecutor();
        asyncTaskThreadPool.setCorePoolSize(100);
        asyncTaskThreadPool.setMaxPoolSize(200);
        asyncTaskThreadPool.setThreadFactory(new ThreadFactory() {

            private final AtomicLong index = new AtomicLong(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "Async-task-pool-thread-" + index.getAndIncrement());
            }
        });
       // asyncTaskThreadPool.initialize();//當爲bean的時候不須要調用此方法,裝載容器的時候回自動調用
        return asyncTaskThreadPool;
    }

以上兩種方式定義線程池的時候在定義異步方法能夠不執行線程池。定義以下:

@Async
    public void test(){
        System.out.println(Thread.currentThread().getName());
        
    }

此時Spring會自動使用以上定義的線程池執行此方法。使用以上兩種配置輸出結果依次是:

Async-task-pool-thread-1
Async-task-override-pool-thread-1

方法三 在Async註解中執行線程池名稱

異步任務定義以下:

@Async(value = "asyncTaskThreadPool")
    public void asyncTask2() {
        LOGGER.info("AsyncTask2 start.");
        LOGGER.info(Thread.currentThread().getName());
        LOGGER.info("AsyncTask2 finished.");
    }

此時Spring會在上下文中找名稱爲asyncTaskThreadPool的線程池來執行此任務。

相似的能夠自定義Scheduled的線程池,須要實現的配置接口爲:SchedulingConfigurer。方法相似。

4.Async返回操做結果

異步任務能夠經過定義返回類型爲Future來實現返回值,定義以下:

@Async
    public Future<String> asyncTaskWithResult() {
        LOGGER.info("AsyncTaskWithResult start.");
        try {
            Thread.sleep(1000 * 10);
        } catch (Exception e) {
            return new AsyncResult<>("error" + e.getMessage());
        }
        LOGGER.info("AsyncTaskWithResult finished.");

        return new AsyncResult<>("success");
    }

5.編寫單元測試測試功能

單元測試代碼以下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class AsyncApplicationTests {

    @Autowired
    private AsyncTaskService asyncTaskService;

    @Test
    public void asyncTest() throws Exception{
        Future<String> future = asyncTaskService.asyncTaskWithResult();

        while (!future.isDone()) {
            System.out.println("Wait asyncTaskWithResult.");
            Thread.sleep(1000);
        }
        System.out.println("asyncTaskWithResult result is:" + future.get());
        System.out.println("asyncTask finished.");

    }

}

輸出內容以下:

Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
Wait asyncTaskWithResult.
AsyncTaskWithResult finished.
asyncTaskWithResult result is:success
asyncTask finished.
相關文章
相關標籤/搜索