SpringBoot调用方法异步执行
自定义线程池:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务;
* 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
* 当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝;
*/
@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
/**
* 核心线程数(默认线程数)
*/
private static final int corePoolSize = 20;
/**
* 最大线程数
*/
private static final int maxPoolSize = 100;
/**
* 允许线程空闲时间(单位:默认为秒)
*/
private static final int keepAliveTime = 10;
/**
* 缓冲队列大小
*/
private static final int queueCapacity = 200;
/**
* 线程池名前缀
*/
private static final String threadNamePrefix = "Async-Service-";
@Bean("taskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setKeepAliveSeconds(keepAliveTime);
executor.setThreadNamePrefix(threadNamePrefix);
// 线程池对拒绝任务的处理策略
// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 初始化
executor.initialize();
return executor;
}
}
方法异步执行:
代码中的 @Async(“taskExecutor”) 对应我们自定义线程池中的 @Bean(“taskExecutor”) ,表示使用我们自定义的线程池。
@Async("taskExecutor")
如下方式会使@Async失效:
1、异步方法使用 static 修饰。
2、异步类没有被 Spring 容器管理。
3、异步方法不能与被调用的异步方法在同一个类中。
4、SpringBoot框架必须在配置类中增加 @EnableAsync 注解。
异步失效场景一:
testAsync2方法并不会进行异步调用,因为与被调用的方法在同一个类中。
@Slf4j
@Component
public class DemoService {
@Async("taskExecutor")
public void testAsync() {
log.info("testAsync");
testAsync2();
}
@Async("taskExecutor")
public void testAsync2() {
log.info("testAsync2");
}
}
代码编写
扩展ThreadPoolTaskExecutor:
每次提交线程的时候都会将当前线程池的运行状况打印出来。
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskExecutor.class);
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
return;
}
logger.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
// 线程名称前缀
this.getThreadNamePrefix(),
prefix,
// 任务总数
threadPoolExecutor.getTaskCount(),
// 已完成数
threadPoolExecutor.getCompletedTaskCount(),
// 活跃线程数
threadPoolExecutor.getActiveCount(),
// 队列大小
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
线程池配置:
@Configuration
@EnableAsync
public class ExecutorConfig {
private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);
@Bean
public Executor asyncServiceExecutor() {
logger.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
// 配置核心线程数
executor.setCorePoolSize(5);
// 配置最大线程数
executor.setMaxPoolSize(5);
// 配置队列大小
executor.setQueueCapacity(99999);
// 配置线程池中的线程的名称前缀
executor.setThreadNamePrefix("async-service-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
业务逻辑代码:
将Service层的服务异步化,使用 @Async(“asyncServiceExecutor”)
,表明executeAsync方法进入线程池运行。
public interface AsyncService {
// 执行异步任务
void executeAsync();
}
@Service
public class AsyncServiceImpl implements AsyncService {
private static final Logger logger = LoggerFactory.getLogger(AsyncServiceImpl.class);
@Async("asyncServiceExecutor")
@Override
public void executeAsync() {
logger.info("start executeAsync");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
logger.info("end executeAsync");
}
}
Http服务:
@RestController
public class DemoController {
private static final Logger logger = LoggerFactory.getLogger(DemoController.class);
@Autowired
private AsyncService asyncService;
@GetMapping("/demo")
public String demo() {
logger.info("start submit");
//调用service层的任务
asyncService.executeAsync();
logger.info("end submit");
return "demo";
}
}
测试:
2020-09-25 17:52:47.400 INFO 6552 --- [async-service-1] com.example.service.AsyncServiceImpl : start executeAsync
2020-09-25 17:52:47.495 INFO 6552 --- [nio-8080-exec-1] com.example.controller.DemoController : start submit
2020-09-25 17:52:47.495 INFO 6552 --- [nio-8080-exec-1] c.e.c.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [34], completedTaskCount [26], activeCount [5], queueSize [3]
2020-09-25 17:52:47.495 INFO 6552 --- [nio-8080-exec-1] com.example.controller.DemoController : end submit
2020-09-25 17:52:47.559 INFO 6552 --- [async-service-2] com.example.service.AsyncServiceImpl : end executeAsync
2020-09-25 17:52:47.559 INFO 6552 --- [async-service-2] com.example.service.AsyncServiceImpl : start executeAsync
2020-09-25 17:52:47.658 INFO 6552 --- [nio-8080-exec-3] com.example.controller.DemoController : start submit
2020-09-25 17:52:47.658 INFO 6552 --- [nio-8080-exec-3] c.e.c.VisiableThreadPoolTaskExecutor : async-service-, 2. do submit,taskCount [35], completedTaskCount [27], activeCount [5], queueSize [3]
2020-09-25 17:52:47.658 INFO 6552 --- [nio-8080-exec-3] com.example.controller.DemoController : end submit
注意这一行日志: 2. do submit,taskCount [35], completedTaskCount [27], activeCount [5], queueSize [3]
说明提交任务到线程池的时候,调用的是submit(Callable task)
这个方法,当前已经提交了35
个任务,完成了27
个,当前有5
个线程在处理任务,还剩3
个任务在队列中等待,线程池的基本情况一路了然。