本文主要记录了@Async注解的使用,@Async主要用于开启异步任务。
项目环境:JDK1.8、SpringBoot、MySQL。前置条件:需要创建一个SpringBoot项目。
行文逻辑按照如下顺序:
- 默认线程池使用
@Async- 自定义线程池
- 异步失效
- 带返回值的异步处理
默认线程池
在新建的SpringBoot项目中添加@EnableAsync,此时启动类代码如下:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
controller层代码如下:
public class AsyncController {
@Resource
private IAsyncService asyncService;
@GetMapping("default_thread.do")
public void threadTest(){
asyncService.syncMethod();
asyncService.asyncMethod();
}
}
service层代码如下:接口定义代码省略。
@Service
@Slf4j
public class AsyncServiceImpl implements IAsyncService {
@Override
public void syncMethod() {
log.info("syncMethod threadName = {}", Thread.currentThread().getName());
}
@Override
@Async
public void asyncMethod() {
log.info("asyncMethod threadName = {}", Thread.currentThread().getName());
}
}
此时运行结果如下:

在没有配置线程池的情况下,springboot 会启动默认的线程池来开启任务。在上图的运行结果中也可以看到异步方法以异步运行了。使用了注解的线程名和controller中打印的线程名称不一样。
自定义线程池
由于默认线程池的弊端,在实际开发中,一般不会采用默认的线程池配置,因此需要我们自定义线程池配置,新增配置类,代码如下:
@Bean(name = "myTaskAsyncPool")
public Executor myTaskAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(10);
executor.setThreadNamePrefix("my-thread-pool-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
此时在service层添加如下方法:
@Override
@Async
public void asyncMethod() {
log.info("asyncMethod threadName = {}", Thread.currentThread().getName());
}
// 新增的代码
@Override
@Async("myTaskAsyncPool")
public void asyncMethodCustomThreadPool() {
log.info("asyncMethodCustomThreadPool threadName = {}", Thread.currentThread().getName());
}
运行结果如下:
从运行结果上可以看到自定义配置的线程池起作用了。
异步失效
@Async失效的场景:在当前类方法中自己调用自己的方法。
这是因为 @Async 注解的实现是基于 Spring 的 AOP,而 AOP 的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过 Spring 容器。除此之外方法的定义必须是 public。
@Resource
@Lazy
private IAsyncService asyncService;
@Override
@Async
public void asyncInvalid() {
log.info("asyncInvalid threadName = {}", Thread.currentThread().getName());
this.asyncMethod();
}
@Override
@Async
public void asyncValid2() {
log.info("asyncValid2 threadName = {}", Thread.currentThread().getName());
// 通过自动注入来实现
asyncService.asyncMethodCustomThreadPool();
}
从下图的运行结果中可以看到:asyncInvalid()异步失效了。通过自动注入的方法则没有。

异步任务的返回值
在前面的几个例子中示例的都是不需要返回值的情况,但是在实际的项目中、往往需要异步任务的返回值、根据这一步的返回结果做下一步处理,用@Async修饰的方法需要借助Future来实现,代码如下:
@Override
public Future<String> asyncReturnFuture() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 模拟异步返回结果 可走db查询 redis缓存查询等
AsyncResult<String> asyncResult = new AsyncResult<String>("return in async");
return asyncResult;
}
@GetMapping("future_thread.do")
public String futureThreadTest() throws ExecutionException, InterruptedException {
Future<String> stringFuture = asyncService.asyncReturnFuture();
return stringFuture.get();
}
异步任务出现异常的情况
有时候异步任务的执行也不总是顺利的、总会出现异常的情况。针对无返回值的情况,可以修改配置类的代码:
@Configuration
public class ThreadPoolConfiguration implements AsyncConfigurer {
@Bean(name = "myTaskAsyncPool")
public Executor myTaskAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(10);
executor.setThreadNamePrefix("my-thread-pool-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public Executor getAsyncExecutor() {
return AsyncConfigurer.super.getAsyncExecutor();
}
// 针对无返回值的
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncExceptionHandler();
}
}
@Slf4j
class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
log.info("Exception message - " + ex.getMessage());
log.info("Method name - " + method.getName());
for (Object param : params) {
log.info("Parameter value - " + param);
}
}
}
修改完配置类之后,在service层代码如下:
@Override
@Async
public void asyncException() {
throw new RuntimeException("asyncException");
}
@Override
@Async
public Future<String> asyncReturnFutureException() {
try {
TimeUnit.SECONDS.sleep(1);
throw new RuntimeException("asyncReturnFutureException");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
针对需要返回值的异步任务,我们可以在调用方通过try...catch来进行处理。在controller层添加如下代码:
@GetMapping("future_void_exception.do")
public void futureThreadExceptionTest1(){
try {
asyncService.asyncException();
}catch (Exception e){
log.error("futureThreadExceptionTest error",e);
}
}
@GetMapping("future_return_exception.do")
public String futureThreadExceptionTest2(){
try {
Future<String> stringFuture = asyncService.asyncReturnFutureException();
return stringFuture.get();
} catch (InterruptedException e) {
log.error("futureThreadExceptionTest error",e);
return "出错了";
} catch (ExecutionException e) {
log.error("futureThreadExceptionTest error",e);
return "出错了";
}catch (Exception e){
log.error("futureThreadExceptionTest error",e);
return "出错了" + e.getMessage();
}
}
分别访问上述路由,可以看到异常处理的起作用了。
参考资料: