我的项目中有三种方法都带有@Scheduled注释,其中一种是cron表达式,另外两种是固定延迟。注释看起来像:
方法1:
@Scheduled(fixedDelay = 20000)
@Async
protected void checkBrokenEngines() {
方法2:
@Scheduled(fixedRate = 20000)
@Async
public void checkAvailableTasks() throws Exception {
方法3:
@Scheduled(cron = "0 0 2 * * ?")
protected void deleteOldData() {
[以前我有一个问题,当checkBrokenEngines
和checkAvailableTasks
方法执行缓慢时,下一个执行直到上一个执行结束才发生。从StackOverflow中阅读文档和此处的一些主题,我发现我的项目的pool size设置有误,并且这些方法未使用async进行注释。 (异步是为了下一次执行开始,即使旧的没有结束,因为这不会在我的应用程序中引起任何问题)
现在出现另一个问题,这是我的问题:
执行deleteOldData()
方法时,其他两种方法都不会运行直到完成。在看到此方法阻止了其他两个方法的执行之后,我将该方法注释为异步,然后,即使该方法需要花费时间来执行,其他两个方法也总是在规定的时间内正确调用。为什么?以我的理解,这应该不会发生,因为这些方法是通过异步记录的,并且池中有足够的空间来执行它们。
PS:deleteOldData()
不能异步。它必须在上一次执行完成后才开始。
编辑1:
异步执行器配置:
@Override
public AsyncTaskExecutor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(50);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(10000);
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
计划的任务由ThreadPoolTaskScheduler
处理,它的默认池大小为1。仅当将它们注释为@Async
时,执行才会传递到AsyncTaskExecutor
中,为您配置了具有更大池大小的专用执行程序。
要在ThreadPoolTaskScheduler
类中配置@Configuration
:
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
scheduler.setPoolSize(50);
return scheduler ;
}
这是因为@Async任务默认是由计划执行程序提交的,其大小默认为1。
我修改了AsyncTaskExecutor
执行程序的提交方法:
@Bean
AsyncConfigurer asyncConfigurer() {
return new AsyncConfigurer() {
@Override
public AsyncTaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(){
@Override
public <T> Future<T> submit(Callable<T> task) {
System.out.println("async task was started by thread -- "+Thread.currentThread().getName());
return super.submit(task);
}
};
executor.setThreadNamePrefix("custom-async-exec");
executor.setCorePoolSize(2);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
};
}
和输出。
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
async task was started by thread -- scheduling-1
因此,有1个线程进入默认的Shedulers池scheduling-1
,并且每当繁忙时就无法启动/提交新的@Async
任务。定义@Bean
ThreadPoolTaskExecutor或添加spring.task.scheduling.pool.size=x
。
编辑
这里是用于可视化的简单测试:
@Component
public static class Jobs{
@Scheduled(fixedDelay = 1500)
@Async
public void job1(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Scheduled(fixedDelay = 1500)
@Async
public void job2(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Scheduled(initialDelay = 10000, fixedDelay = 5000)
public void blocking(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
visualvm
的可视化>
[红色'箭头'显示了blocking()
作业的开始。尽管scheduling-1
Thread
为此被阻止,但也无法提交job1()
和job2()
@Scheduled
和@Scheduled
与@Async
都使用不同的线程池执行程序。@Scheduled
将使用默认的单线程执行程序来调度这两种任务。