@Async做异步
注解说明:
- 在方法上使用该@Async注解,申明该方法是一个异步任务
- 在类上面使用该@Async注解,申明该类中的所有方法都是异步任务
- 使用此注解的方法的类对象,必须是spring管理下的bean对象
- 需要在主类)(启动类)上开启异步配置,即,配置上@EnableAsync注解;
- 如果不指定线程池的名称,则使用Spring默认的线程池,Spring默认的线程池为SimpleAsyncTaskExecutor
- 方法上一旦标记了这个@Async注解,当其它线程调用这个方法时,就会开启一个新的子线程去异步处理该业务逻辑
- 可以把@Transactional注解放到内部的需要进行事务的方法上。避免放在外部导致事务管不到异步线程
SimpleAsyncTaskExecutor默认线程池说明
默认线程池采用spring-boot-autoconfigure中task包的TaskExecutionProperties中的配置
2.3.2.RELEASE中的配置为
默认核心线程数:8
最大线程数:Integet.MAX_VALUE
队列使用LinkedBlockingQueue
容量是:Integet.MAX_VALUE
空闲线程保留时间:60s
线程池拒绝策略:AbortPolicy
存在的问题:并发情况下,会无限创建线程
可以修改默认线程池的配置
spring:
task:
execution:
pool:
max-size: 6
core-size: 3
keep-alive: 3s
queue-capacity: 1000
thread-name-prefix: xgss-thread
也可以采用自定义线程池,可以查看示例
@Async的原理
@Async 的原理是通过 Spring AOP 动态代理 的方式来实现的。
Spring容器启动初始化bean时,判断类中是否使用了@Async注解,如果使用了则为其创建切入点和切入点处理器,根据切入点创建代理,在线程调用@Async注解标注的方法时,会调用代理,执行切入点处理器invoke方法,将方法的执行提交给线程池中的另外一个线程来处理,从而实现了异步执行。
所以,需要注意的一个错误用法是,如果a方法调用它同类中的标注@Async的b方法,是不会异步执行的,因为从a方法进入调用的都是该类对象本身,不会进入代理类。
因此,相同类中的方法调用带@Async的方法是无法异步的,这种情况仍然是同步。
示例
自定义线程池
可以在@Been注解中指定线程池的名称,如果线程池名称跟默认线程池名称一样都是taskExcutor就会使用我们自己的配置
@Configuration
@Data
public class ExecutorConfig{
/**
* 核心线程
*/
private int corePoolSize;
/**
* 最大线程
*/
private int maxPoolSize;
/**
* 队列容量
*/
private int queueCapacity;
/**
* 保持时间
*/
private int keepAliveSeconds;
/**
* 名称前缀
*/
private String preFix;
@Bean("xgssExecutor")
public Executor myExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(preFix);
executor.setRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
启动类开启异步
@EnableAsync
@SpringBootApplication
public class ManageApplication {
//...
}
方法上使用注解来使其异步执行
@Component
public class MyAsyncTask {
@Async("xgssExecutor") //使用自定义的线程池(执行器),如果不指定执行器名称走spring默认线程池
public void asyncCpsItemImportTask(Long platformId, String jsonList){
//...具体业务逻辑
}
}
如果使用默认的线程池名字,@Async注解上不需指定线程池名称:
异步任务需要返回值的情况
@Async("MyExecutor")
public Future<Map<Long, List>> queryMap(List ids) {
List<> result = businessService.queryMap(ids);
..............
Map<Long, List> resultMap = Maps.newHashMap();
...
return new AsyncResult<>(resultMap);
}
调用异步方法的示例:
public Map<Long, List> asyncProcess(List<BindDeviceDO> bindDevices,List<BindStaffDO> bindStaffs, String dccId) {
Map<Long, List> finalMap =null;
// 返回值:
Future<Map<Long, List>> asyncResult = MyService.queryMap(ids);
try {
finalMap = asyncResult.get();
} catch (Exception e) {
...
}
return finalMap;
}