使用spring @Scheduled的问题

问题描述 投票:7回答:4

我的项目中有三种方法都带有@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() {

[以前我有一个问题,当checkBrokenEnginescheckAvailableTasks方法执行缓慢时,下一个执行直到上一个执行结束才发生。从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);
}
java spring multithreading spring-boot
4个回答
2
投票

计划的任务由ThreadPoolTaskScheduler处理,它的默认池大小为1。仅当将它们注释为@Async时,执行才会传递到AsyncTaskExecutor中,为您配置了具有更大池大小的专用执行程序。

要在ThreadPoolTaskScheduler类中配置@Configuration

@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
    ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
    scheduler.setPoolSize(50);
    return scheduler ;
}

2
投票

这是因为@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的可视化>

enter image description here

[红色'箭头'显示了blocking()作业的开始。尽管scheduling-1 Thread为此被阻止,但也无法提交job1()job2()


1
投票

@Scheduled@Scheduled@Async都使用不同的线程池执行程序。@Scheduled将使用默认的单线程执行程序来调度这两种任务。


© www.soinside.com 2019 - 2024. All rights reserved.