Spring Boot 中的异步调用
通常我们开发的程序都是同步调用的,即程序按照代码的顺序一行一行的逐步往下执行,每一行代码都必须等待上一行代码执行完毕才能开始执行。而异步编程则没有这个限制,代码的调用不再是阻塞的。所以在一些情景下,通过异步编程可以提高效率,提升接口的吞吐量。这节将介绍如何在Spring Boot中进行异步编程。
要开启异步支持,首先得在Spring Boot入口类上加上@EnableAsync注解:
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main ( String [ ] args) {
SpringApplication . run ( DemoApplication . class , args) ;
}
}
新建service包,并创建TestService:
@Service
public class TestService {
private Logger logger = LoggerFactory . getLogger ( this . getClass ( ) ) ;
@Async
public void asyncMethod ( ) {
sleep ( ) ;
logger. info ( "异步方法内部线程名称:{}" , Thread . currentThread ( ) . getName ( ) ) ;
}
public void syncMethod ( ) {
sleep ( ) ;
}
private void sleep ( ) {
try {
TimeUnit . SECONDS. sleep ( 2 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
}
上面的Service中包含一个异步方法asyncMethod(开启异步支持后,只需要在方法上加上@Async注解便是异步方法了)和同步方法syncMethod。sleep方法用于让当前线程阻塞2秒钟。
因为异步的原因,程序并没有被sleep方法阻塞,这就是异步调用的好处。同时异步方法内部会新启一个线程来执行
默认情况下的异步线程池配置使得线程不能被重用,每次调用异步方法都会新建一个线程,我们可以自己定义异步线程池来优化。
@Configuration
public class AsyncPoolConfig {
@Bean
public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor ( ) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor ( ) ;
executor. setCorePoolSize ( 20 ) ;
executor. setMaxPoolSize ( 200 ) ;
executor. setQueueCapacity ( 25 ) ;
executor. setKeepAliveSeconds ( 200 ) ;
executor. setThreadNamePrefix ( "asyncThread" ) ;
executor. setWaitForTasksToCompleteOnShutdown ( true ) ;
executor. setAwaitTerminationSeconds ( 60 ) ;
executor. setRejectedExecutionHandler ( new ThreadPoolExecutor. CallerRunsPolicy ( ) ) ;
executor. initialize ( ) ;
return executor;
}
}
要使用该线程池,只需要在@Async注解上指定线程池Bean名称即可:
@Service
public class TestService {
. . . . . .
@Async ( "asyncThreadPoolTaskExecutor" )
public void asyncMethod ( ) {
. . . . . .
}
. . . . . .
}
处理异步回调
如果异步方法具有返回值的话,需要使用Future来接收回调值。我们修改TestService的asyncMethod方法,给其添加返回值:
@Async ( "asyncThreadPoolTaskExecutor" )
public Future < String > asyncMethod ( ) {
sleep ( ) ;
logger. info ( "异步方法内部线程名称:{}" , Thread . currentThread ( ) . getName ( ) ) ;
return new AsyncResult < > ( "hello async" ) ;
}
Future接口的get方法用于获取异步调用的返回值。
通过返回结果我们可以看出Future的get方法为阻塞方法,只有当异步方法返回内容了,程序才会继续往下执行。get还有一个get(long timeout, TimeUnit unit)重载方法,我们可以通过这个重载方法设置超时时间,即异步方法在设定时间内没有返回值的话,直接抛出java.util.concurrent.TimeoutException异常。
比如设置超时时间为60秒:
String result = stringFuture. get ( 60 , TimeUnit . SECONDS) ;