Spring中實現異步調用的方式有哪些?

語言: CN / TW / HK

一位3年工作經驗的小夥伴被問到這樣一道面試題,説Spring中實現異步調用的方式有哪些?

今天,我給大家分享一下我的理解。

在Spring中,實現異步調用主要有三種方式,分別是註解方式、內置線程池方式和自定義線程池方式。

1、註解方式

可以在配置類和方法上加特定註解。首先,在配置類加上@EnableAsync來啟用異步註解,

如代碼所示:

@EnableAsync//啟用異步支持
@Configuration
public class AppConfig {
}

然後,使用@Async註解標記需要異步執行的方法,

如代碼所示:

@Async
void doSomething() {
    // this will be run asynchronously
}

@Async
void doSomething(String s) {
    // this will be run asynchronously
}

@Async
Future<String> returnSomething(int i) {
    // this will be run asynchronously
}

使用@Async標記的異步方法可以帶參數,也可以帶有返回值。返回值類型必須是java.util.concurrent.Future或其子類,可以是以下3種類型:

1)由Java原生API提供的Future。

2)由Spring提供的ListenableFuture後者AsyncResult。

3)Java 8提供的CompletableFuture。

需要説明的是,@Async默認會使用SimpleAsyncTaskExecutor來執行,而這個線程池不會複用線程。所以,通常要使用異步處理,我們都會自定義線程池。

2、內置線程池方式

可以使用Spring內置的線程池來實現異步調用,比如ThreadPoolTaskExecutor 和SimpleAsyncTaskExecutor。Spring提供了許多TaskExecutor的內置實現。下面簡單介紹5種內置的線程池。

1)SimpleAsyncTaskExecutor:它不會複用線程,每次調用都是啟動一個新線程。

2)ConcurrentTaskExecutor:它是Java API中Executor實例的適配器。

3)ThreadPoolTaskExecutor:這個線程池是最常用的。它公開了用於配置的bean屬性,並將它包裝在TaskExecutor中。

4)WorkManagerTaskExecutor:它基於CommonJ WorkManager來實現的,並且是在Spring上下文中的WebLogic或WebSphere中設置CommonJ線程池的工具類。

5)DefaultManagedTaskExecutor:主要用於支持JSR-236兼容的運行時環境,它是使用JNDI獲得ManagedExecutorService,作為CommonJ WorkManager的替代方案。

通常情況下,ThreadPoolTaskExecuto最為常用,只要當ThreadPoolTaskExecutor不能滿足需求時,可以使用ConcurrentTaskExecutor。如果在代碼中聲明瞭多個線程池,Spring會默認按照以下搜索順序來調用線程池:

第一步,檢查上下文中的唯一TaskExecutor Bean。

第二步,檢查名為“ taskExecutor”的Executor Bean。

第三步,以上都無法無法處理,就會使用SimpleAsyncTaskExecutor來執行。

3、自定義線程池方式

可以通過實現AsyncConfigurer接口或者直接繼承AsyncConfigurerSupport類來自定義線程池。但是非完全託管的Bean和完全託管的Bean實現方式有點小差異。

首先,來看非完全託管的Spring Bean,實現方式如代碼所示:

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        executor.initialize();//手動初始化
        return executor;
    }

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

在這段代碼中,ThreadPoolTaskExecutor不是完全託管的Spring bean。

然後,來看完全託管的Spring Bean,實現方式如代碼所示:

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

    @Bean
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(7);
        executor.setMaxPoolSize(42);
        executor.setQueueCapacity(11);
        executor.setThreadNamePrefix("MyExecutor-");
        //executor.initialize();//不用手動調用
        return executor;
    }

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

只要在異步方法上添加@Bean註解,不需要手動調用線程池的initialize()方法,在Bean在初始化之後會自動調用。需要注意的是,在同級類中直接調用異步方法無法實現異步。

以上就是我對Spring實現異步調用的理解。