快速搞懂Spring中實現(xiàn)異步調(diào)用的方式有哪些?
? 一位3年工作經(jīng)驗的小伙伴被問到這樣一道面試題,說Spring中實現(xiàn)異步調(diào)用的方式有哪些?
今天,我給大家分享一下我的理解。
在Spring中,實現(xiàn)異步調(diào)用主要有三種方式,分別是注解方式、內(nèi)置線程池方式和自定義線程池方式。
1、注解方式
可以在配置類和方法上加特定注解。首先,在配置類加上@EnableAsync來啟用異步注解,
如代碼所示:
@EnableAsync//啟用異步支持 @Configuration public class AppConfig { }
然后,使用@Async注解標(biāo)記需要異步執(zhí)行的方法,
如代碼所示:
@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標(biāo)記的異步方法可以帶參數(shù),也可以帶有返回值。返回值類型必須是java.util.concurrent.Future或其子類,可以是以下3種類型:
1)由Java原生API提供的Future。
2)由Spring提供的ListenableFuture后者AsyncResult。
3)Java 8提供的CompletableFuture。
需要說明的是,@Async默認(rèn)會使用SimpleAsyncTaskExecutor來執(zhí)行,而這個線程池不會復(fù)用線程。所以,通常要使用異步處理,我們都會自定義線程池。
2、內(nèi)置線程池方式
可以使用Spring內(nèi)置的線程池來實現(xiàn)異步調(diào)用,比如ThreadPoolTaskExecutor 和SimpleAsyncTaskExecutor。Spring提供了許多TaskExecutor的內(nèi)置實現(xiàn)。下面簡單介紹5種內(nèi)置的線程池。
1)SimpleAsyncTaskExecutor:它不會復(fù)用線程,每次調(diào)用都是啟動一個新線程。
2)ConcurrentTaskExecutor:它是Java API中Executor實例的適配器。
3)ThreadPoolTaskExecutor:這個線程池是最常用的。它公開了用于配置的bean屬性,并將它包裝在TaskExecutor中。
4)WorkManagerTaskExecutor:它基于CommonJ WorkManager來實現(xiàn)的,并且是在Spring上下文中的WebLogic或WebSphere中設(shè)置CommonJ線程池的工具類。
5)DefaultManagedTaskExecutor:主要用于支持JSR-236兼容的運行時環(huán)境,它是使用JNDI獲得ManagedExecutorService,作為CommonJ WorkManager的替代方案。
通常情況下,ThreadPoolTaskExecuto最為常用,只要當(dāng)ThreadPoolTaskExecutor不能滿足需求時,可以使用ConcurrentTaskExecutor。如果在代碼中聲明了多個線程池,Spring會默認(rèn)按照以下搜索順序來調(diào)用線程池:
第一步,檢查上下文中的唯一TaskExecutor Bean。
第二步,檢查名為“ taskExecutor”的Executor Bean。
第三步,以上都無法無法處理,就會使用SimpleAsyncTaskExecutor來執(zhí)行。
3、自定義線程池方式
可以通過實現(xiàn)AsyncConfigurer接口或者直接繼承AsyncConfigurerSupport類來自定義線程池。但是非完全托管的Bean和完全托管的Bean實現(xiàn)方式有點小差異。
首先,來看非完全托管的Spring Bean,實現(xiàn)方式如代碼所示:
@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,實現(xiàn)方式如代碼所示:
@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();//不用手動調(diào)用 return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new MyAsyncUncaughtExceptionHandler(); } }
只要在異步方法上添加@Bean注解,不需要手動調(diào)用線程池的initialize()方法,在Bean在初始化之后會自動調(diào)用。需要注意的是,在同級類中直接調(diào)用異步方法無法實現(xiàn)異步。
以上就是我對Spring實現(xiàn)異步調(diào)用的理解。