@Scheduled 定时任务同一时间执行问题

springBoot 定时任务@scheduled同时执行

在使用过程中,发现多个任务设计同一时间执行会出现只有一个任务在执行其它任务都无法执行的情况。

新建测试类

由于项目为SpringBoot框架为了让定时任务生效需要在启动类上面加上@EnableScheduling以开启对定时任务的支持

package com.joker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * @program: jokerLin
 * @description
 * @author: Joker
 * @create: 2021-05-06 16:17
 **/
@SpringBootApplication
@EnableScheduling
public class JokerShineApplicaton {

    public static void main(String[] args) {
        SpringApplication.run(JokerShineApplicaton.class, args);
    }

}

新建测试类里面定义2个方法demo1和demo2。两个方法里面没有任何操作只打印当前时间和线程名

package com.joker.scheduled;
import io.swagger.annotations.ApiOperation;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

/**
 * @program: jokerLin
 * @description
 * @author: Joker
 * @create: 2021-05-06 16:42
 **/
@Component
public class ScheduledController {

    @Scheduled(cron = "0 0/5 * * * ? ")//每个五分钟执行
    @ApiOperation("定时任务一")
    public void demo1(){
        String name = Thread.currentThread().getName() ;
        System.out.println("当前时间:"+ LocalDateTime.now()+"  任务execute1对应的线程名: "+name);
        try {
            System.out.println("任务一");
            Thread.sleep(5000);
            System.out.println("任务一结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Scheduled(cron = "0 0/5 * * * ? ")//每个五分钟执行
    @ApiOperation("定时任务二")
    public void demo2(){
        String name = Thread.currentThread().getName() ;
        System.out.println("当前时间:"+ LocalDateTime.now()+"  任务execute2对应的线程名: "+name);
        try {
            System.out.println("任务二");
            Thread.sleep(2000);
            System.out.println("任务二结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

按正常的理解此时运行demo1和demo2打印的线程名应该不一致才对、结果确不尽然。

当前时间:2021-05-06T16:20:00.005  任务execute1对应的线程名: scheduling-1
任务一
任务一结束
当前时间:2021-05-06T16:20:05.005  任务execute2对应的线程名: scheduling-1
任务二
任务二结束
当前时间:2021-05-06T16:25:00.001  任务execute2对应的线程名: scheduling-1
任务二
任务二结束
当前时间:2021-05-06T16:25:02.002  任务execute1对应的线程名: scheduling-1
任务一
任务一结束

同一时间间隔的2个定时任务(都设置了5分钟运行一次)只会运行结束一个才会运行第二个(等待状态),并且时线程名字是一样的。

问题解析

因此有理由怀疑springboot创建线程的时使用了newSingleThreadExecutor。带着这个疑问,debug后,我们首先在demo1方法中打个断点看下调用类和线程池看下是什么情况。
debug结果
通过上图我们可以发现springboot创建的线程池poolSize确实是1,当前活动线程数(activethreads)为1。

查看@EnableScheduling:

package org.springframework.scheduling.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({SchedulingConfiguration.class})
@Documented
public @interface EnableScheduling {
}

可以看到EnableScheduling是将SchedulingConfiguration这个类实例化并注入到springboot容器中,我们继续跟踪下去看下改配置类执行什么操作。

package org.springframework.scheduling.annotation;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;

@Configuration
@Role(2)
public class SchedulingConfiguration {
    public SchedulingConfiguration() {
    }

    @Bean(
        name = {"org.springframework.context.annotation.internalScheduledAnnotationProcessor"}
    )
    @Role(2)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
        return new ScheduledAnnotationBeanPostProcessor();
    }
}

可以看出里面注入了org.springframework.context.annotation.internalScheduledAnnotationProcessor这个类

查看类ScheduledTaskRegistrar

protected void scheduleTasks() {
        if (this.taskScheduler == null) {
            this.localExecutor = Executors.newSingleThreadScheduledExecutor();
            this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
        }

        Iterator var1;
        if (this.triggerTasks != null) {
            var1 = this.triggerTasks.iterator();

            while(var1.hasNext()) {
                TriggerTask task = (TriggerTask)var1.next();
                this.addScheduledTask(this.scheduleTriggerTask(task));
            }
        }

可以发现当taskScheduler对象为空时默认创建的是newSingleThreadScheduledExecutor()

大概的意思是如果没有指定TaskScheduler则会创建一个单线程的默认调度器。
因此需要自己创建一个TaskScheduler。

解决方法

指定TaskScheduler则会创建一个单线程的默认调度器。

@Bean
public TaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
    taskScheduler.setPoolSize(50);
    return taskScheduler;
}

需要把这一段代码放进启动类即可

package com.joker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

/**
 * @program: jokerLin
 * @description
 * @author: Joker
 * @create: 2021-05-06 16:17
 **/
@SpringBootApplication
@EnableScheduling
public class JokerShineApplicaton {

    public static void main(String[] args) {
        SpringApplication.run(JokerShineApplicaton.class, args);
    }

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(50);
        return taskScheduler;
    }
}

再次测试结果如下:

当前时间:2021-05-06T16:20:00.004  任务execute1对应的线程名: taskScheduler-2
任务一
当前时间:2021-05-06T16:20:00.004  任务execute1对应的线程名: taskScheduler-1
任务二
任务二结束
任务一结束

线程名发生改变了,问题得到了解决。

其他

1.@Bean允许使用注解@Role设置角色

2.newSingleThreadScheduledExecutor:产生一个ScheduledExecutorService对象,这个对象的线程池大小为1,如果任务多于一个,任务将按先后顺序执行。

3.spring线程池任务调度类 ThreadPoolTaskScheduler:

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值