日常问题记录: Spring @Scheduled任务不生效

标题:日常问题记录: Spring @Scheduled任务不生效

问题描述

在维护一个Spring Boot应用程序的过程中,我们发现一些使用@Scheduled注解的方法并没有按预期执行。而其中一个@Scheduled注解的代码却触发了。

问题分析

当前问题排查

经过深入调查,我们发现问题的根本原因在于Spring默认的调度器是一个单线程的SimpleAsyncTaskExecutor。而那个可以正常触发的代码是一个有while循环的常驻任务,它在条件不通过的情况下,将一直占用当前线程,导致其他定时任务都不能执行。

问题延伸

当有多个定时任务需要执行时,如果一个任务执行时间过长,它会阻塞后续任务的执行。此外,如果应用程序的其他部分对线程资源有较高的需求,单一的调度线程可能不足以满足所有需求。

解决方案

为了解决这个问题,我们决定自定义@Scheduled任务的调度器,以便能够更好地控制线程资源的使用。我们创建了一个@Configuration类,实现了SchedulingConfigurer接口,并重写了configureTasks方法。

自定义调度器

ScheduleConfig类中,我们首先通过反射获取了所有带有@Scheduled注解的方法,然后根据这些方法的数量来设置线程池的大小。这样可以确保每个定时任务都有一个专用的线程来执行,从而避免了任务之间的相互阻塞。

代码实现

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 获取所有带有@Scheduled注解的方法
        Method[] methods = BatchProperties.Job.class.getMethods();
        int defaultPoolSize = 3; // 默认的线程池大小
        int corePoolSize = 0; // 核心线程池大小

        // 计算需要的线程数
        if (methods.length > 0) {
            for (Method method : methods) {
                Scheduled annotation = method.getAnnotation(Scheduled.class);
                if (annotation != null) {
                    corePoolSize++;
                }
            }
        }
        // 如果计算出的核心线程数小于默认值,则使用默认值
        if (defaultPoolSize > corePoolSize) {
            corePoolSize = defaultPoolSize;
        }

        // 设置自定义的调度器
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(corePoolSize));
    }
}

在上述代码中,我们首先定义了一个默认的线程池大小defaultPoolSize。然后,我们遍历所有带有@Scheduled注解的方法,根据注解的方法数量来确定核心线程池的大小corePoolSize。最后,我们使用Executors.newScheduledThreadPool创建了一个自定义的调度器,并将其设置给ScheduledTaskRegistrar

结论

在之后的开发中如还有场景使用@Scheduled的话,一定要考虑线程的占用问题,以及是否有必要自定义调度器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值