1. 定义

当使用Spring框架进行开发时,我们可能会遇到一些耗时的操作,比如发送邮件、调用第三方接口等。如果直接在主线程中执行这些操作,会导致主线程阻塞,影响系统的响应速度,一般会用到多线程来处理,但线程的创建与销毁是一个开销大的操作,因此一般采用创建线程池的方式来维护线程。但项目一般比较复杂,就会存在一个问题,每个开发人员都维护了一套线程池或每个功能模块都维护了线程池,这样就会导致项目中存在多个线程池,由于线程池中的线程数量是根据cpu的核心数来创建的,多个线程池的意义就不大,且维护了多套。

为了解决这个问题,Spring提供了@Async注解来支持异步执行任务,而且可以统一管理线程池。

2. @Async注解的使用

@Async注解可以用来修饰方法或类。当修饰方法时,会将该方法标记为一个异步方法,可以在调用时在新的线程中进行执行。当修饰类时,将会对该类的所有方法生效。

2.1. 解析使用

  1. 使用@EnableAsync开启注解支持
  2. 创建线程池Executor,也可以在第3点new()出来
  3. 实现AsyncConfigurer,重写getAsyncExecutor获取线程池方法和getAsyncUncaughtExceptionHandler处理异常方法
    getAsyncExecutor会返回@Async需要的线程池,如果为空的话spring则会自行创建一个线程,则相当于没有使用线程池,因此此处需要特别注意
    getAsyncUncaughtExceptionHandler异常处理方法,可以用来记录异步线程失败的处理,如记录日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Configuration
@EnableAsync
public class BaseAsyncConfigurer implements AsyncConfigurer {

/**
* 配置了此线程池后spring就不会每次异步创建线程,
* 若此处没有配置,spring则会先在容器中找线程池,
* 若找到只有一个,则使用,
* 若有多个,则必须在@Async中指定线程池的名字,否则报错
* 若没有找到,则创建一个线程
*
* 此处线程采用了懒加载,在第一次调用时才会真正的调用并返回值
* @return
*/
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程池数量,方法: 返回可用处理器的数量+1,,默认为io密集型。
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors()+1);
//最大线程数量
executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors()*5);
//线程池的队列容量
executor.setQueueCapacity(Runtime.getRuntime().availableProcessors()*2);
//线程名称的前缀
executor.setThreadNamePrefix("this-excutor-");
// setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//初始化,好像也可以不要
executor.initialize();
return executor;
}
/*异步任务中异常处理*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (Throwable ex, Method method, Object... params)->{
System.out.println("class#method: " + method.getDeclaringClass().getName() + "#" + method.getName());
System.out.println("type : " + ex.getClass().getName());
System.out.println("exception : " + ex.getMessage());
};
}

}

使用的方法上直接加上注解@Async即可

1
2
3
4
@Async
public void sendMessage(){
//处理异步事件
}