当你在service层需要启动异步线程去执行某些分支任务,又不希望显式使用Thread等线程相关类,只想专注于实现业务逻辑代码开发,可以使用@Async异步注解。
1、 使用@Async 异步注解
Controller层方法:
//批量插入用户
@RequestMapping("/user/addSystemUser")
public void batchAddUser(@RequestParam(value = "usernameList[]",required=false) List usernameList){
for (int i = 0; i < usernameList.size(); i++) {
//使用异步线程执行每一个用户新增
userService.addUser(usernameList.get(i));
}
}
Service层方法,是真正使用@Async异步注解的:
@Transactional(propagation = Propagation.NESTED) //如果当前事务存在,则在嵌套事务中执行。如果没有,就新建一个事务;
@Async
public void addUser(SystemUser sessionUser, List<Container> containerList, Timestamp createTime){
// do save one User
}
2、@Async异步注解使用注意点
a)该注解可使用在类、接口(包括注释类型)或枚举声明
b)当被标注在方法级别时,该方法返回值要么是void,或java.util.concurrent.Future接口实现类
3、自定义异步注解真正的执行线程类(可选)
如果懒得自己写一个线程执行类的话,我猜SpringBoot会默认设置一些org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor类的配置。
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 创建自定义配置的线程池
*/
@Configuration
@EnableAsync
public class MyTaskExecutePool implements AsyncConfigurer {
private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(ASyncTask.class);
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程池大小
executor.setCorePoolSize(20);
//最大线程数
executor.setMaxPoolSize(40);
//队列容量
executor.setQueueCapacity(50);
//活跃时间
executor.setKeepAliveSeconds(30);
//线程名字前缀
executor.setThreadNamePrefix("MyTaskExecutePool-");
// setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
* 异步任务中异常处理
* @return
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) {
//纪录log错误日志
LOG.error("exception method:"+arg1.getName()+";"+arg0.getMessage(), arg0);
}
};
}
}
4、日志监控异步线程活动(可选)
logback.xml配置文件中设置 %thread 纪录线程执行名
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- 级别过滤器。如果日志级别低于WARN,将被过滤掉。 ALL TRACE DEBUG INFO WARN ERROR-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %msg - %file:%line%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>